This commit is contained in:
nephacks
2025-06-04 03:22:50 +02:00
parent f234f23848
commit f12416cffd
14243 changed files with 6446499 additions and 26 deletions

151
hammer/AnchorMgr.cpp Normal file
View File

@@ -0,0 +1,151 @@
//====== Copyright © 1996-2005, Valve Corporation, All rights reserved. =======
//
// Purpose:
//
//=============================================================================
#include "stdafx.h"
#include "AnchorMgr.h"
static int ProcessAnchorHorz( int originalCoord, int originalParentSize[2], EAnchorHorz eAnchor, int parentWidth, int parentHeight )
{
if ( eAnchor == k_eAnchorLeft )
return originalCoord;
else
return parentWidth - (originalParentSize[0] - originalCoord);
}
static int ProcessAnchorVert( int originalCoord, int originalParentSize[2], EAnchorVert eAnchor, int parentWidth, int parentHeight )
{
if ( eAnchor == k_eAnchorTop )
return originalCoord;
else
return parentHeight - (originalParentSize[1] - originalCoord);
}
CAnchorDef::CAnchorDef( int dlgItemID, ESimpleAnchor eSimpleAnchor )
{
Init( NULL, dlgItemID, eSimpleAnchor );
}
CAnchorDef::CAnchorDef( int dlgItemID, EAnchorHorz eLeftSide, EAnchorVert eTopSide, EAnchorHorz eRightSide, EAnchorVert eBottomSide )
{
Init( NULL, dlgItemID, eLeftSide, eTopSide, eRightSide, eBottomSide );
}
CAnchorDef::CAnchorDef( HWND hWnd, ESimpleAnchor eSimpleAnchor )
{
Init( hWnd, -1, eSimpleAnchor );
}
CAnchorDef::CAnchorDef( HWND hWnd, EAnchorHorz eLeftSide, EAnchorVert eTopSide, EAnchorHorz eRightSide, EAnchorVert eBottomSide )
{
Init( hWnd, -1, eLeftSide, eTopSide, eRightSide, eBottomSide );
}
void CAnchorDef::Init( HWND hWnd, int dlgItemID, ESimpleAnchor eSimpleAnchor )
{
if ( eSimpleAnchor == k_eSimpleAnchorBottomRight )
{
Init( hWnd, dlgItemID, k_eAnchorRight, k_eAnchorBottom, k_eAnchorRight, k_eAnchorBottom );
}
else if ( eSimpleAnchor == k_eSimpleAnchorAllSides )
{
Init( hWnd, dlgItemID, k_eAnchorLeft, k_eAnchorTop, k_eAnchorRight, k_eAnchorBottom );
}
else if ( eSimpleAnchor == k_eSimpleAnchorStretchRight )
{
Init( hWnd, dlgItemID, k_eAnchorLeft, k_eAnchorTop, k_eAnchorRight, k_eAnchorTop );
}
else if ( eSimpleAnchor == k_eSimpleAnchorRightSide )
{
Init( hWnd, dlgItemID, k_eAnchorRight, k_eAnchorTop, k_eAnchorRight, k_eAnchorTop );
}
else if ( eSimpleAnchor == k_eSimpleAnchorBottomSide )
{
Init( hWnd, dlgItemID, k_eAnchorLeft, k_eAnchorBottom, k_eAnchorLeft, k_eAnchorBottom );
}
}
void CAnchorDef::Init( HWND hWnd, int dlgItemID, EAnchorHorz eLeftSide, EAnchorVert eTopSide, EAnchorHorz eRightSide, EAnchorVert eBottomSide )
{
m_hInputWnd = hWnd;
m_DlgItemID = dlgItemID;
m_AnchorLeft = eLeftSide;
m_AnchorTop = eTopSide;
m_AnchorRight = eRightSide;
m_AnchorBottom = eBottomSide;
}
CAnchorMgr::CAnchorMgr()
{
}
void CAnchorMgr::Init( HWND hParentWnd, CAnchorDef *pAnchors, int nAnchors )
{
m_Anchors.CopyArray( pAnchors, nAnchors );
m_hParentWnd = hParentWnd;
// Figure out the main window's size.
RECT rcParent, rcItem;
GetWindowRect( m_hParentWnd, &rcParent );
m_OriginalParentSize[0] = rcParent.right - rcParent.left;
m_OriginalParentSize[1] = rcParent.bottom - rcParent.top;
// Get all the subitem positions.
for ( int i=0; i < m_Anchors.Count(); i++ )
{
CAnchorDef *pAnchor = &m_Anchors[i];
if ( pAnchor->m_hInputWnd )
pAnchor->m_hWnd = pAnchor->m_hInputWnd;
else
pAnchor->m_hWnd = GetDlgItem( m_hParentWnd, pAnchor->m_DlgItemID );
if ( !pAnchor->m_hWnd )
continue;
GetWindowRect( pAnchor->m_hWnd, &rcItem );
POINT ptTopLeft;
ptTopLeft.x = rcItem.left;
ptTopLeft.y = rcItem.top;
ScreenToClient( m_hParentWnd, &ptTopLeft );
pAnchor->m_OriginalPos[0] = ptTopLeft.x;
pAnchor->m_OriginalPos[1] = ptTopLeft.y;
pAnchor->m_OriginalPos[2] = ptTopLeft.x + (rcItem.right - rcItem.left);
pAnchor->m_OriginalPos[3] = ptTopLeft.y + (rcItem.bottom - rcItem.top);
}
}
void CAnchorMgr::OnSize()
{
// Get the new size.
int width, height;
RECT rcParent;
GetWindowRect( m_hParentWnd, &rcParent );
width = rcParent.right - rcParent.left;
height = rcParent.bottom - rcParent.top;
// Move each item.
for ( int i=0; i < m_Anchors.Count(); i++ )
{
CAnchorDef *pAnchor = &m_Anchors[i];
if ( !pAnchor->m_hWnd )
continue;
RECT rcNew;
rcNew.left = ProcessAnchorHorz( pAnchor->m_OriginalPos[0], m_OriginalParentSize, pAnchor->m_AnchorLeft, width, height );
rcNew.right = ProcessAnchorHorz( pAnchor->m_OriginalPos[2], m_OriginalParentSize, pAnchor->m_AnchorRight, width, height );
rcNew.top = ProcessAnchorVert( pAnchor->m_OriginalPos[1], m_OriginalParentSize, pAnchor->m_AnchorTop, width, height );
rcNew.bottom = ProcessAnchorVert( pAnchor->m_OriginalPos[3], m_OriginalParentSize, pAnchor->m_AnchorBottom, width, height );
SetWindowPos( pAnchor->m_hWnd, NULL, rcNew.left, rcNew.top, rcNew.right-rcNew.left, rcNew.bottom-rcNew.top, SWP_NOZORDER );
InvalidateRect( pAnchor->m_hWnd, NULL, false );
}
}

86
hammer/AnchorMgr.h Normal file
View File

@@ -0,0 +1,86 @@
//====== Copyright © 1996-2005, Valve Corporation, All rights reserved. =======
//
// Purpose:
//
//=============================================================================
#ifndef ANCHORMGR_H
#define ANCHORMGR_H
#ifdef _WIN32
#pragma once
#endif
#include "tier1/utlvector.h"
enum ESimpleAnchor
{
k_eSimpleAnchorAllSides,
k_eSimpleAnchorBottomRight, // The whole control follows the bottom right.
k_eSimpleAnchorStretchRight, // Only grow in width.
k_eSimpleAnchorRightSide,
k_eSimpleAnchorBottomSide
};
enum EAnchorHorz
{
k_eAnchorLeft, // Anchor this side of the control to the left of its parent window.
k_eAnchorRight // Anchor this side of the control to the right of its parent window.
};
enum EAnchorVert
{
k_eAnchorTop, // Anchor this side of the control to the top of its parent window.
k_eAnchorBottom // Anchor this side of the control to the bottom of its parent window.
};
class CAnchorDef
{
public:
friend class CAnchorMgr;
// You can use the first constructor for simple cases.
CAnchorDef() {}
CAnchorDef( int dlgItemID, ESimpleAnchor eSimpleAnchor );
CAnchorDef( HWND hWnd, ESimpleAnchor eSimpleAnchor );
CAnchorDef( int dlgItemID, EAnchorHorz eLeftSide=k_eAnchorLeft, EAnchorVert eTopSide=k_eAnchorTop, EAnchorHorz eRightSide=k_eAnchorRight, EAnchorVert eBottomSide=k_eAnchorBottom );
CAnchorDef( HWND hWnd, EAnchorHorz eLeftSide=k_eAnchorLeft, EAnchorVert eTopSide=k_eAnchorTop, EAnchorHorz eRightSide=k_eAnchorRight, EAnchorVert eBottomSide=k_eAnchorBottom );
// Only one of hWnd or dlgItemID should be set.
void Init( HWND hWnd, int dlgItemID, ESimpleAnchor eSimpleAnchor );
void Init( HWND hWnd, int dlgItemID, EAnchorHorz eLeftSide, EAnchorVert eTopSide, EAnchorHorz eRightSide, EAnchorVert eBottomSide );
public:
int m_DlgItemID;
HWND m_hInputWnd; // Either this or m_DlgItemID is set.
EAnchorHorz m_AnchorLeft, m_AnchorRight;
EAnchorVert m_AnchorTop, m_AnchorBottom;
private:
int m_OriginalPos[4]; // left, top, right, bottom
HWND m_hWnd;
};
//-----------------------------------------------------------------------------
// Purpose: This class helps a resizable window resize and move its controls
// whenever the window is resized. First, you call Init() to tell it how
// you want all the child windows anchored, then just call OnSize() whenever
// the parent window is sized.
//-----------------------------------------------------------------------------
class CAnchorMgr
{
public:
CAnchorMgr();
void Init( HWND hParentWnd, CAnchorMgr::CAnchorDef *pAnchors, int nAnchors );
// Call this when the parent window's size changes and it'll resize all the subcontrols.
void OnSize();
private:
CUtlVector<CAnchorMgr::CAnchorDef> m_Anchors;
HWND m_hParentWnd;
int m_OriginalParentSize[2]; // wide, tall
};
#endif // ANCHORMGR_H

1234
hammer/DispShore.cpp Normal file

File diff suppressed because it is too large Load Diff

79
hammer/DispShore.h Normal file
View File

@@ -0,0 +1,79 @@
//========= Copyright © 1996-2004, Valve LLC, All rights reserved. ============
//
//
//=============================================================================
#ifndef DISPSHORE_H
#define DISPSHORE_H
#pragma once
#include "MapHelper.h"
#include "MapFace.h"
#include "MapOverlay.h"
#include "MapOverlayTrans.h"
class CHelperInfo;
class CMapEntity;
struct ShoreFaceData_t
{
CMapFace *m_pFaces[4]; // The hammer faces the points reside on.
Vector m_vecPoints[4]; // The shore face points.
Vector2D m_vecTexCoords[4]; // The shore texture coordinates.
bool m_bAdjWinding;
};
struct ShoreSegment_t
{
Vector m_vecPoints[2]; // Shore segment points.
Vector m_vecNormals[2]; // Shore segment normals.
Vector2D m_vecTexCoords[4]; // Shore segment texture coordinates.
EditDispHandle_t m_hDisp; // Displacement the shore segment was created from.
float m_flWaterZ;
ShoreFaceData_t m_WorldFace;
ShoreFaceData_t m_WaterFace;
// ?
Vector m_vecCenter;
unsigned int m_iStartPoint;
bool m_bTouch;
bool m_bCreated;
};
struct Shoreline_t
{
int m_nShorelineId;
CUtlVector<ShoreSegment_t> m_aSegments; // List of shore segments making up the shore line.
CUtlVector<int> m_aSortedSegments; // List of shore segments sorted (for connectivity).
CUtlVector<CMapOverlay> m_aOverlays;
float m_flLength; // Total length of the shore line.
ShoreEntityData_t m_ShoreData;
Shoreline_t();
~Shoreline_t();
void AddSegment( Vector &vecPoint0, Vector &vecPoint1, Vector &vecNormal, float flWaterZ, CMapFace *pWaterFace, EditDispHandle_t hDisp );
};
class IDispShoreManager
{
public:
virtual bool Init( void ) = 0;
virtual void Shutdown( void ) = 0;
// Shoreline management.
virtual int GetShorelineCount( void ) = 0;
virtual Shoreline_t *GetShoreline( int nShorelineId ) = 0;
virtual void AddShoreline( int nShorelineId ) = 0;
virtual void RemoveShoreline( int nShorelineId ) = 0;
virtual void BuildShoreline( int nShorelineId, CUtlVector<CMapFace*> &aFaces, CUtlVector<CMapFace*> &aWaterFaces ) = 0;
virtual void Draw( CRender3D *pRender ) = 0;
virtual void DebugDraw( CRender3D *pRender ) = 0;
};
IDispShoreManager *GetShoreManager( void );
#endif // DISPSHORE_H

View File

@@ -0,0 +1,161 @@
//====== Copyright © 1996-2005, Valve Corporation, All rights reserved. =======
//
// Purpose:
//
//=============================================================================
#include "stdafx.h"
#include "FileChangeWatcher.h"
#include "tier1/utldict.h"
#include "filesystem_tools.h"
#include "vstdlib/vstrtools.h"
CFileChangeWatcher::CFileChangeWatcher()
{
m_pCallbacks = NULL;
}
CFileChangeWatcher::~CFileChangeWatcher()
{
Term();
}
void CFileChangeWatcher::Init( ICallbacks *pCallbacks )
{
Term();
m_pCallbacks = pCallbacks;
}
bool CFileChangeWatcher::AddDirectory( const char *pSearchPathBase, const char *pDirName, bool bRecursive )
{
char fullDirName[MAX_PATH];
V_ComposeFileName( pSearchPathBase, pDirName, fullDirName, sizeof( fullDirName ) );
HANDLE hDir = CreateFile( fullDirName, GENERIC_READ, FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS|FILE_FLAG_OVERLAPPED, NULL );
if ( hDir == INVALID_HANDLE_VALUE )
{
Warning( "CFileChangeWatcher::AddDirectory - can't get a handle to directory %s.\n", pDirName );
return false;
}
// Call this once to start the ball rolling.. Next time we call it, it'll tell us the changes that
// have happened since this call.
CDirWatch *pDirWatch = new CDirWatch;
V_strncpy( pDirWatch->m_SearchPathBase, pSearchPathBase, sizeof( pDirWatch->m_SearchPathBase ) );
V_strncpy( pDirWatch->m_DirName, pDirName, sizeof( pDirWatch->m_DirName ) );
V_strncpy( pDirWatch->m_FullDirName, fullDirName, sizeof( pDirWatch->m_FullDirName ) );
pDirWatch->m_hDir = hDir;
pDirWatch->m_hEvent = CreateEvent( NULL, false, false, NULL );
memset( &pDirWatch->m_Overlapped, 0, sizeof( pDirWatch->m_Overlapped ) );
pDirWatch->m_Overlapped.hEvent = pDirWatch->m_hEvent;
if ( !CallReadDirectoryChanges( pDirWatch ) )
{
CloseHandle( pDirWatch->m_hEvent );
CloseHandle( pDirWatch->m_hDir );
delete pDirWatch;
return false;
}
m_DirWatches.AddToTail( pDirWatch );
return true;
}
void CFileChangeWatcher::Term()
{
for ( int i=0; i < m_DirWatches.Count(); i++ )
{
CloseHandle( m_DirWatches[i]->m_hDir );
CloseHandle( m_DirWatches[i]->m_hEvent );
}
m_DirWatches.PurgeAndDeleteElements();
m_pCallbacks = NULL;
}
int CFileChangeWatcher::Update()
{
CUtlDict< int, int > queuedChanges;
int nTotalChanges = 0;
// Check each CDirWatch.
int i = 0;
while ( i < m_DirWatches.Count() )
{
CDirWatch *pDirWatch = m_DirWatches[i];
DWORD dwBytes = 0;
if ( GetOverlappedResult( pDirWatch->m_hDir, &pDirWatch->m_Overlapped, &dwBytes, FALSE ) )
{
// Read through the notifications.
int nBytesLeft = (int)dwBytes;
char *pCurPos = pDirWatch->m_Buffer;
while ( nBytesLeft >= sizeof( FILE_NOTIFY_INFORMATION ) )
{
FILE_NOTIFY_INFORMATION *pNotify = (FILE_NOTIFY_INFORMATION*)pCurPos;
if ( m_pCallbacks )
{
// Figure out what happened to this file.
WCHAR nullTerminated[2048];
int nBytesToCopy = min( pNotify->FileNameLength, 2047 );
memcpy( nullTerminated, pNotify->FileName, nBytesToCopy );
nullTerminated[nBytesToCopy/2] = 0;
char ansiFilename[1024];
V_UnicodeToUTF8( nullTerminated, ansiFilename, sizeof( ansiFilename ) );
// Now add it to the queue. We use this queue because sometimes Windows will give us multiple
// of the same modified notification back to back, and we want to reduce redundant calls.
int iExisting = queuedChanges.Find( ansiFilename );
if ( iExisting == queuedChanges.InvalidIndex() )
{
iExisting = queuedChanges.Insert( ansiFilename, 0 );
++nTotalChanges;
}
}
if ( pNotify->NextEntryOffset == 0 )
break;
pCurPos += pNotify->NextEntryOffset;
nBytesLeft -= (int)pNotify->NextEntryOffset;
}
CallReadDirectoryChanges( pDirWatch );
continue; // Check again because sometimes it queues up duplicate notifications.
}
// Process all the entries in the queue.
for ( int iQueuedChange=queuedChanges.First(); iQueuedChange != queuedChanges.InvalidIndex(); iQueuedChange=queuedChanges.Next( iQueuedChange ) )
{
SendNotification( pDirWatch, queuedChanges.GetElementName( iQueuedChange ) );
}
queuedChanges.Purge();
++i;
}
return nTotalChanges;
}
void CFileChangeWatcher::SendNotification( CFileChangeWatcher::CDirWatch *pDirWatch, const char *pRelativeFilename )
{
// Use this for full filenames although you don't strictly need it..
char fullFilename[MAX_PATH];
V_ComposeFileName( pDirWatch->m_FullDirName, pRelativeFilename, fullFilename, sizeof( fullFilename ) );
m_pCallbacks->OnFileChange( pRelativeFilename, fullFilename );
}
BOOL CFileChangeWatcher::CallReadDirectoryChanges( CFileChangeWatcher::CDirWatch *pDirWatch )
{
return ReadDirectoryChangesW( pDirWatch->m_hDir,
pDirWatch->m_Buffer,
sizeof( pDirWatch->m_Buffer ),
true,
FILE_NOTIFY_CHANGE_FILE_NAME | FILE_NOTIFY_CHANGE_SIZE | FILE_NOTIFY_CHANGE_LAST_WRITE,
NULL,
&pDirWatch->m_Overlapped,
NULL );
}

View File

@@ -0,0 +1,70 @@
//====== Copyright © 1996-2005, Valve Corporation, All rights reserved. =======
//
// Purpose:
//
//=============================================================================
#ifndef FILECHANGEWATCHER_H
#define FILECHANGEWATCHER_H
#ifdef _WIN32
#pragma once
#endif
#include "tier1/utlvector.h"
//-----------------------------------------------------------------------------
// Purpose: This class provides notifications of changes in directories.
// Call AddDirectory to tell it which directories to watch, then
// call Update() periodically to check for updates.
//-----------------------------------------------------------------------------
class CFileChangeWatcher
{
public:
class ICallbacks
{
public:
// Note: this is called if the file is added, removed, or modified. It's up to the app to figure out
// what it wants to do with the change.
virtual void OnFileChange( const char *pRelativeFilename, const char *pFullFilename ) = 0;
};
CFileChangeWatcher();
~CFileChangeWatcher();
void Init( ICallbacks *pCallbacks );
// pSearchPathBase would be like "c:\valve\hl2" and pDirName would be like "materials".
bool AddDirectory( const char *pSearchPathBase, const char *pDirName, bool bRecursive );
void Term();
// Call this periodically to update. It'll call your ICallbacks functions for anything it finds.
// Returns the number of updates it got.
int Update();
private:
class CDirWatch
{
public:
char m_SearchPathBase[MAX_PATH];
char m_DirName[MAX_PATH];
char m_FullDirName[MAX_PATH];
OVERLAPPED m_Overlapped;
HANDLE m_hEvent;
HANDLE m_hDir; // Created with CreateFile.
char m_Buffer[1024 * 16];
};
void SendNotification( CFileChangeWatcher::CDirWatch *pDirWatch, const char *pRelativeFilename );
BOOL CallReadDirectoryChanges( CFileChangeWatcher::CDirWatch *pDirWatch );
private:
// Override these.
CUtlVector<CDirWatch*> m_DirWatches;
ICallbacks *m_pCallbacks;
};
#endif // FILECHANGEWATCHER_H

876
hammer/FilteredComboBox.cpp Normal file
View File

@@ -0,0 +1,876 @@
//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ====
//
// Purpose:
//
//=============================================================================
#include "stdafx.h"
#include "FilteredComboBox.h"
BEGIN_MESSAGE_MAP(CFilteredComboBox, CComboBox)
//{{AFX_MSG_MAP(CFilteredComboBox)
ON_CONTROL_REFLECT_EX(CBN_SELCHANGE, OnSelChange)
ON_CONTROL_REFLECT_EX(CBN_EDITCHANGE, OnEditChange)
ON_CONTROL_REFLECT_EX(CBN_CLOSEUP, OnCloseUp)
ON_CONTROL_REFLECT_EX(CBN_DROPDOWN, OnDropDown)
ON_CONTROL_REFLECT_EX(CBN_SELENDOK, OnSelEndOK)
ON_WM_CTLCOLOR()
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
static const char *s_pStringToMatch = NULL;
static int s_iStringToMatchLen;
// This can help debug events in the combo box.
static int g_iFunctionMarkerEvent = 1;
class CFunctionMarker
{
public:
CFunctionMarker( const char *p )
{
#if 0
m_iEvent = g_iFunctionMarkerEvent++;
char str[512];
Q_snprintf( str, sizeof( str ), "enter %d: %s\n", m_iEvent, p );
OutputDebugString( str );
m_p = p;
#endif
}
~CFunctionMarker()
{
#if 0
char str[512];
Q_snprintf( str, sizeof( str ), "exit %d: %s\n", m_iEvent, m_p );
OutputDebugString( str );
#endif
}
const char *m_p;
int m_iEvent;
};
// ------------------------------------------------------------------------------------------------------------ //
// CFilteredComboBox implementation.
// ------------------------------------------------------------------------------------------------------------ //
CFilteredComboBox::CFilteredComboBox( CFilteredComboBox::ICallbacks *pCallbacks )
: m_pCallbacks( pCallbacks )
{
m_hQueuedFont = NULL;
m_bInSelChange = false;
m_bNotifyParent = true;
m_dwTextColor = RGB(0, 0, 0);
m_bOnlyProvideSuggestions = true;
m_hEditControlFont = NULL;
m_bInEnterKeyPressedHandler = false;
}
void CFilteredComboBox::SetSuggestions( CUtlVector<CString> &suggestions, int flags )
{
CreateFonts();
// Verify some of the window styles. This class requires these, and it doesn't get a change to set them
// unless you call Create on it.
// If we use owner draw variable, we get the bug described here: http://support.microsoft.com/kb/813791.
Assert( GetStyle() & CBS_OWNERDRAWFIXED );
Assert( GetStyle() & CBS_HASSTRINGS );
Assert( !( GetStyle() & CBS_SORT ) );
// Copy the list.
m_Suggestions = suggestions;
CString str;
GetWindowText( str );
DWORD sel = GetEditSel();
FillDropdownList( NULL, false );
// Force it to provide the first one if they only want suggestions and the current text in there is not valid.
bool bSelectFirst = ((flags & SETSUGGESTIONS_SELECTFIRST) != 0);
bool bCallback = ((flags & SETSUGGESTIONS_CALLBACK) != 0);
bool bForceFirst = (m_bOnlyProvideSuggestions && FindSuggestion( str ) == -1);
if ( bSelectFirst || bForceFirst )
{
SetCurSel( 0 );
if ( GetCount() > 0 )
{
CString str;
GetLBText( 0, str );
if ( bCallback )
DoTextChangedCallback( str );
}
else
{
m_LastTextChangedValue = "";
}
}
else
{
SetWindowText( str );
SetEditSel( LOWORD( sel ), HIWORD( sel ) );
if ( bCallback )
DoTextChangedCallback( str );
}
SetRedraw( true );
Invalidate();
}
void CFilteredComboBox::AddSuggestion( const CString &suggestion )
{
if ( FindSuggestion( suggestion ) == -1 )
m_Suggestions.AddToTail( suggestion );
}
void CFilteredComboBox::Clear()
{
m_Suggestions.Purge();
SetWindowText( "" );
m_LastTextChangedValue = "";
}
void CFilteredComboBox::ForceEditControlText( const char *pStr )
{
SetWindowText( pStr );
}
void CFilteredComboBox::SelectItem( const char *pStr )
{
if ( !pStr )
{
SetEditControlText( "" );
return;
}
// See if we already have this item selected. If so, don't do anything.
int iCurSel = GetCurSel();
if ( iCurSel != CB_ERR )
{
CString str;
GetLBText( iCurSel, str );
if ( Q_stricmp( pStr, str ) == 0 )
{
// Make sure the edit control has the right text in there. If they called ForceEditControlText,
// then it might not.
CString str;
GetWindowText( str );
if ( Q_stricmp( str, pStr ) != 0 )
{
SetWindowText( pStr );
}
m_LastTextChangedValue = pStr;
return;
}
}
if ( m_bOnlyProvideSuggestions && FindSuggestion( pStr ) == -1 )
{
// This item doesn't match any suggestion. We can get rid of this assert
// if it becomes a nuissance, but for now it's good to note that this
// is a weird situation.
Assert( false );
SetEditControlText( pStr );
return;
}
FillDropdownList( pStr );
}
CString CFilteredComboBox::GetCurrentItem()
{
return m_LastTextChangedValue;
}
void CFilteredComboBox::SetEditControlFont( HFONT hFont )
{
if ( !hFont )
return;
if ( m_bInSelChange )
{
m_hQueuedFont = hFont;
return;
}
CString str;
GetWindowText( str );
DWORD sel = GetEditSel();
InternalSetEditControlFont( hFont, str, sel );
}
void CFilteredComboBox::InternalSetEditControlFont( HFONT hFont, const char *pEditText, DWORD sel )
{
if ( hFont != m_hEditControlFont )
{
CFunctionMarker marker( "InternalSetEditControlFont" );
// Don't let it mess with everything here.
SetRedraw( false );
CRect rcMyRect;
GetWindowRect( rcMyRect );
CWnd *pParent = GetParent();
if ( pParent )
pParent->ScreenToClient( &rcMyRect );
BOOL bWasDropped = GetDroppedState();
m_hEditControlFont = hFont;
SetFont( CFont::FromHandle( m_hEditControlFont ), false );
SetWindowText( pEditText );
SetEditSel( LOWORD( sel ), HIWORD( sel ) );
if ( pParent )
MoveWindow( rcMyRect );
if ( bWasDropped )
ShowDropDown( true );
SetRedraw( true );
Invalidate();
}
}
HFONT CFilteredComboBox::GetEditControlFont() const
{
return m_hEditControlFont;
}
void CFilteredComboBox::SetEditControlTextColor(COLORREF dwColor)
{
m_dwTextColor = dwColor;
}
COLORREF CFilteredComboBox::GetEditControlTextColor() const
{
return m_dwTextColor;
}
void CFilteredComboBox::SetEditControlText( const char *pText )
{
SetWindowText( pText );
}
CString CFilteredComboBox::GetEditControlText() const
{
CString ret;
GetWindowText( ret );
return ret;
}
bool CFilteredComboBox::IsWindowEnabled() const
{
return (BaseClass::IsWindowEnabled() == TRUE);
}
void CFilteredComboBox::EnableWindow( bool bEnable )
{
BaseClass::EnableWindow( bEnable );
}
void CFilteredComboBox::SetOnlyProvideSuggestions( bool bOnlyProvideSuggestions )
{
m_bOnlyProvideSuggestions = bOnlyProvideSuggestions;
}
void CFilteredComboBox::FillDropdownList( const char *pInitialSel, bool bEnableRedraw )
{
CFunctionMarker marker( "FillDropdownList" );
SetRedraw( FALSE );
ResetContent();
// Fill the box with the initial set of values.
CUtlVector<CString> items;
GetItemsMatchingString( "", items );
for ( int i=0; i < items.Count(); i++ )
AddString( items[i] );
if ( pInitialSel )
{
CString str = pInitialSel;
if ( m_bOnlyProvideSuggestions )
{
str = GetBestSuggestion( pInitialSel );
if ( !InternalSelectItemByName( pInitialSel) )
{
Assert( false );
}
}
else
{
// Make sure we're putting the item they requested in there.
if ( !InternalSelectItemByName( str ) )
{
// Add the typed text to the combobox here otherwise it'll select the nearest match when they drop it down with the mouse.
AddString( str );
InternalSelectItemByName( str );
}
}
DoTextChangedCallback( str );
}
if ( bEnableRedraw )
{
SetRedraw( TRUE );
Invalidate();
}
}
LRESULT CFilteredComboBox::DefWindowProc(
UINT message,
WPARAM wParam,
LPARAM lParam
)
{
// We handle the enter key specifically because the default combo box behavior is to
// reset the text and all this stuff we don't want.
if ( message == WM_KEYDOWN )
{
if ( wParam == '\r' )
{
OnEnterKeyPressed( NULL );
return 0;
}
else if ( wParam == 27 )
{
// Escape..
OnEscapeKeyPressed();
return 0;
}
}
return BaseClass::DefWindowProc( message, wParam, lParam );
}
BOOL CFilteredComboBox::PreCreateWindow( CREATESTRUCT& cs )
{
// We need these styles in order for owner draw to work.
// If we use CBS_OWNERDRAWVARIABLE, then we run into this bug: http://support.microsoft.com/kb/813791.
cs.style |= CBS_OWNERDRAWFIXED | CBS_HASSTRINGS;
cs.style &= ~CBS_SORT;
return BaseClass::PreCreateWindow( cs );
}
void CFilteredComboBox::OnEnterKeyPressed( const char *pForceText )
{
if ( m_bInEnterKeyPressedHandler )
return;
CFunctionMarker marker( "OnEnterKeyPressed" );
m_bInEnterKeyPressedHandler = true;
// Must do this before ShowDropDown because that will change these variables underneath us.
CString szTypedText;
DWORD sel;
if ( pForceText )
{
szTypedText = pForceText;
sel = 0;
}
else
{
GetWindowText( szTypedText );
sel = GetEditSel();
}
CRect rcMyRect;
GetWindowRect( rcMyRect );
CWnd *pParent = GetParent();
if ( pParent )
pParent->ScreenToClient( &rcMyRect );
SetRedraw( false );
ShowDropDown( FALSE );
// They can get into here a variety of ways. Editing followed by enter. Editing+arrow keys, followed by enter, etc.
if ( m_bOnlyProvideSuggestions )
{
CString str;
if ( FindSuggestion( szTypedText ) == -1 && m_pCallbacks->OnUnknownEntry( szTypedText ) )
{
// They want us to KEEP this unknown entry, so add it to our list and select it.
m_Suggestions.AddToTail( szTypedText );
str = szTypedText;
}
else
{
// They returned false, so do the default behavior: go to the best match we can find.
str = GetBestSuggestion( szTypedText );
}
DoTextChangedCallback( str );
FillDropdownList( str, false );
if ( GetCurSel() == CB_ERR )
SetCurSel( 0 );
}
else
{
FillDropdownList( szTypedText, false );
SetWindowText( szTypedText );
SetEditSel( LOWORD(sel), HIWORD(sel) );
}
// Restore our window if necessary.
if ( pParent )
MoveWindow( &rcMyRect );
SetRedraw( true );
Invalidate();
DoTextChangedCallback( GetEditControlText() );
m_bInEnterKeyPressedHandler = false;
}
void CFilteredComboBox::OnEscapeKeyPressed()
{
// Fill it with everything and force it to select whatever we last selected.
m_bInEnterKeyPressedHandler = true;
ShowDropDown( FALSE );
m_bInEnterKeyPressedHandler = false;
FillDropdownList( m_LastTextChangedValue, true );
}
BOOL CFilteredComboBox::OnDropDown()
{
CFunctionMarker marker( "OnDropDown" );
// This is necessary to keep the cursor from disappearing.
SendMessage( WM_SETCURSOR, 0, 0 );
return !m_bNotifyParent;
}
//-----------------------------------------------------------------------------
// Purpose: Attaches this object to the given dialog item.
//-----------------------------------------------------------------------------
void CFilteredComboBox::SubclassDlgItem(UINT nID, CWnd *pParent)
{
//
// Disable parent notifications for CControlBar-derived classes. This is
// necessary because these classes result in multiple message reflections
// unless we return TRUE from our message handler.
//
if (pParent->IsKindOf(RUNTIME_CLASS(CControlBar)))
{
m_bNotifyParent = false;
}
else
{
m_bNotifyParent = true;
}
BaseClass::SubclassDlgItem(nID, pParent);
}
BOOL CFilteredComboBox::OnSelChange()
{
if ( !m_bInSelChange )
{
CFunctionMarker marker( "OnSelChange" );
CString strOriginalText;
GetWindowText( strOriginalText );
DWORD dwOriginalEditSel = GetEditSel();
m_bInSelChange = true;
int iSel = GetCurSel();
if ( iSel != CB_ERR )
{
CString str;
GetLBText( iSel, str );
strOriginalText = str;
DoTextChangedCallback( str );
}
m_bInSelChange = false;
if ( m_hQueuedFont )
{
HFONT hFont = m_hQueuedFont;
m_hQueuedFont = NULL;
m_bInSelChange = false;
InternalSetEditControlFont( hFont, strOriginalText, dwOriginalEditSel );
}
}
//
// Despite MSDN's lies, returning FALSE here allows the parent
// window to hook the notification message as well, not TRUE.
//
return !m_bNotifyParent;
}
BOOL CFilteredComboBox::OnCloseUp()
{
if ( !m_bInEnterKeyPressedHandler )
{
CFunctionMarker marker( "OnCloseUp" );
CString str;
if ( GetCurSel() == CB_ERR || GetCount() == 0 )
str = m_LastTextChangedValue;
else
GetLBText( GetCurSel(), str );
OnEnterKeyPressed( str );
}
//
// Despite MSDN's lies, returning FALSE here allows the parent
// window to hook the notification message as well, not TRUE.
//
return !m_bNotifyParent;
}
BOOL CFilteredComboBox::OnSelEndOK()
{
//
// Despite MSDN's lies, returning FALSE here allows the parent
// window to hook the notification message as well, not TRUE.
//
return !m_bNotifyParent;
}
BOOL CFilteredComboBox::OnEditChange()
{
CFunctionMarker marker( "OnEditChange" );
// Remember the text in the edit control because we're going to slam the
// contents of the list and we'll want to put the text back in.
CString szTypedText;
DWORD dwEditSel;
GetWindowText( szTypedText );
dwEditSel = GetEditSel();
// Show all the matching autosuggestions.
CUtlVector<CString> items;
GetItemsMatchingString( szTypedText, items );
SetRedraw( FALSE );
ResetContent();
for ( int i=0; i < items.Count(); i++ )
{
AddString( items[i] );
}
// Add the typed text to the combobox here otherwise it'll select the nearest match when they drop it down with the mouse.
if ( !m_bOnlyProvideSuggestions && FindSuggestion( szTypedText ) == -1 )
AddString( szTypedText );
// Note: for arcane and unspeakable MFC reasons, the placement of this call is VERY sensitive.
// For example, if CTargetNameComboBox changes from a bold font to a normal font, then if this
// call comes before ResetContent(), it will resize the dropdown listbox to a small size and not
// size it back until it is cloesd and opened again.
ShowDropDown();
SetRedraw( TRUE );
Invalidate();
// Possibly tell the app about this change.
if ( m_bOnlyProvideSuggestions )
{
if ( FindSuggestion( szTypedText ) != -1 )
DoTextChangedCallback( szTypedText );
}
else
{
DoTextChangedCallback( szTypedText );
}
// Put the text BACK in there.
SetWindowText( szTypedText );
SetEditSel( LOWORD( dwEditSel ), HIWORD( dwEditSel ) );
//
// Despite MSDN's lies, returning FALSE here allows the parent
// window to hook the notification message as well, not TRUE.
//
return !m_bNotifyParent;
}
int CFilteredComboBox::FindSuggestion( const char *pTest ) const
{
for ( int i=0; i < m_Suggestions.Count(); i++ )
{
if ( Q_stricmp( m_Suggestions[i], pTest ) == 0 )
return i;
}
return -1;
}
CString CFilteredComboBox::GetBestSuggestion( const char *pTest )
{
// If it's an exact match, use that.
if ( FindSuggestion( pTest ) != -1 )
return pTest;
// Look for the first autocomplete suggestion.
CUtlVector<CString> matches;
GetItemsMatchingString( pTest, matches );
if ( matches.Count() > 0 )
return matches[0];
// Ok, fall back to the last known good one.
return m_LastTextChangedValue;
}
CFont& CFilteredComboBox::GetNormalFont()
{
CreateFonts();
return m_NormalFont;
}
void CFilteredComboBox::GetItemsMatchingString( const char *pStringToMatch, CUtlVector<CString> &matchingItems )
{
for ( int i=0; i < m_Suggestions.Count(); i++ )
{
if ( MatchString( pStringToMatch, m_Suggestions[i] ) )
matchingItems.AddToTail( m_Suggestions[i] );
}
s_pStringToMatch = pStringToMatch;
s_iStringToMatchLen = V_strlen( pStringToMatch );
matchingItems.Sort( &CFilteredComboBox::SortFn );
s_pStringToMatch = NULL;
}
int CFilteredComboBox::SortFn( const CString *pItem1, const CString *pItem2 )
{
// If one of them matches the prefix we're looking at, then that one should be listed first.
// Otherwise, just do an alphabetical sort.
bool bPrefixMatch1=false, bPrefixMatch2=false;
if ( s_pStringToMatch )
{
bPrefixMatch1 = ( V_strnistr( *pItem1, s_pStringToMatch, s_iStringToMatchLen ) != NULL );
bPrefixMatch2 = ( V_strnistr( *pItem2, s_pStringToMatch, s_iStringToMatchLen ) != NULL );
}
if ( bPrefixMatch1 == bPrefixMatch2 )
{
return Q_stricmp( *pItem1, *pItem2 );
}
else
{
return bPrefixMatch1 ? -1 : 1;
}
}
bool CFilteredComboBox::MatchString( const char *pStringToMatchStart, const char *pTestStringStart )
{
if ( !pStringToMatchStart || pStringToMatchStart[0] == 0 )
return true;
while ( *pTestStringStart )
{
const char *pStringToMatch = pStringToMatchStart;
const char *pTestString = pTestStringStart;
while ( 1 )
{
// Skip underscores in both strings.
while ( *pStringToMatch == '_' )
++pStringToMatch;
while ( *pTestString == '_' )
++pTestString;
// If we're at the end of pStringToMatch with no mismatch, then treat this as a prefix match.
// If we're at the end of pTestString, but pStringToMatch has more to go, then it's not a match.
if ( *pStringToMatch == 0 )
return true;
else if ( *pTestString == 0 )
break;
// Match this character.
if ( toupper( *pStringToMatch ) != toupper( *pTestString ) )
break;
++pStringToMatch;
++pTestString;
}
++pTestStringStart;
}
return false;
}
//-----------------------------------------------------------------------------
// Purpose: Called before painting to override default colors.
// Input : pDC - DEvice context being painted into.
// pWnd - Control asking for color.
// nCtlColor - Type of control asking for color.
// Output : Returns the handle of a brush to use as the background color.
//-----------------------------------------------------------------------------
HBRUSH CFilteredComboBox::OnCtlColor(CDC *pDC, CWnd *pWnd, UINT nCtlColor)
{
HBRUSH hBrush = CComboBox::OnCtlColor(pDC, pWnd, nCtlColor);
if (nCtlColor == CTLCOLOR_EDIT)
{
pDC->SetTextColor(m_dwTextColor);
}
return(hBrush);
}
void CFilteredComboBox::DoTextChangedCallback( const char *pText )
{
// Sometimes it'll call here from a few places in a row. Only pass the result
// to the owner once.
if ( Q_stricmp( pText, m_LastTextChangedValue ) == 0 )
return;
m_LastTextChangedValue = pText;
m_pCallbacks->OnTextChanged( pText );
}
void CFilteredComboBox::CreateFonts()
{
//
// Create a normal and bold font.
//
if (!m_NormalFont.m_hObject)
{
CFont *pFont = GetFont();
if (pFont)
{
LOGFONT LogFont;
pFont->GetLogFont(&LogFont);
m_NormalFont.CreateFontIndirect(&LogFont);
}
}
}
void CFilteredComboBox::MeasureItem(LPMEASUREITEMSTRUCT pStruct)
{
HFONT hFont;
CFont *pFont = GetFont();
if ( pFont )
hFont = *pFont;
else
hFont = (HFONT)GetStockObject( DEFAULT_GUI_FONT );
CFont *pActualFont = CFont::FromHandle( hFont );
if ( pActualFont )
{
LOGFONT logFont;
pActualFont->GetLogFont( &logFont );
pStruct->itemHeight = abs( logFont.lfHeight ) + 5;
}
else
{
pStruct->itemHeight = 16;
}
}
void CFilteredComboBox::DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct)
{
if ( GetCount() == 0 )
return;
CString str;
GetLBText( lpDrawItemStruct->itemID, str );
CDC dc;
dc.Attach( lpDrawItemStruct->hDC );
// Save these values to restore them when done drawing.
COLORREF crOldTextColor = dc.GetTextColor();
COLORREF crOldBkColor = dc.GetBkColor();
// If this item is selected, set the background color
// and the text color to appropriate values. Erase
// the rect by filling it with the background color.
if ( (lpDrawItemStruct->itemAction | ODA_SELECT) && (lpDrawItemStruct->itemState & ODS_SELECTED) )
{
dc.SetTextColor( ::GetSysColor(COLOR_HIGHLIGHTTEXT) );
dc.SetBkColor( ::GetSysColor(COLOR_HIGHLIGHT) );
dc.FillSolidRect( &lpDrawItemStruct->rcItem, ::GetSysColor(COLOR_HIGHLIGHT) );
}
else
{
dc.FillSolidRect(&lpDrawItemStruct->rcItem, crOldBkColor);
}
CFont *pOldFont = dc.SelectObject( &m_NormalFont );
// Draw the text.
RECT rcDraw = lpDrawItemStruct->rcItem;
rcDraw.left += 1;
dc.DrawText( str, -1, &rcDraw, DT_LEFT|DT_SINGLELINE|DT_VCENTER );
// Restore stuff.
dc.SelectObject( pOldFont );
dc.SetTextColor(crOldTextColor);
dc.SetBkColor(crOldBkColor);
dc.Detach();
}
bool CFilteredComboBox::InternalSelectItemByName( const char *pName )
{
int i = FindStringExact( -1, pName );
if ( i == CB_ERR )
{
return false;
}
else
{
SetCurSel( i );
CString str;
GetWindowText( str );
if ( Q_stricmp( str, pName ) != 0 )
SetWindowText( pName );
return true;
}
}

206
hammer/FilteredComboBox.h Normal file
View File

@@ -0,0 +1,206 @@
//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
//=============================================================================//
#ifndef FILTERED_COMBO_BOX_H
#define FILTERED_COMBO_BOX_H
#ifdef _WIN32
#pragma once
#endif
#include "utlvector.h"
#pragma warning( disable: 4355 )
// Flags for the SetSuggestions call.
#define SETSUGGESTIONS_SELECTFIRST 0x0001 // Select the first item in the list.
#define SETSUGGESTIONS_CALLBACK 0x0002 // Calls OnTextChanged for whatever it winds up selecting.
// CFilteredComboBox is a glorified EDIT control.
// The user can type stuff into the edit control, and it will provide autocomplete suggestions
// in its combo box. The user of this class provides those suggestions.
//
// To use this class:
//
// 1. Implement CFilteredComboBox::ICallbacks
// 2. Call SetSuggestions to set the list of autocomplete suggestions.
// 3. Call SetOnlyProvideSuggestions to tell it how to behave.
//
// NOTE: Use CComboBox functions with caution! You could screw up the CFilteredComboBox's operation.
class CFilteredComboBox : public CComboBox
{
typedef CComboBox BaseClass;
public:
// Implement this to get updates about the state.
class ICallbacks
{
public:
// Called when the text in the box changes.
virtual void OnTextChanged( const char *pText ) = 0;
// This is sort of a backdoor for "only provide suggestions" mode. Normally, it'll only call
// OnTextChanged with entries that are in the suggestions list. But, it will call OnUnknownEntry
// if they type in something that's not in your suggestion list first. If you return TRUE, it
// will add that entry to the suggestions list. If you return FALSE (the default behavior),
// it will find the closest match to what the user typed and use that.
virtual bool OnUnknownEntry( const char *pText ) { return false; }
};
CFilteredComboBox( CFilteredComboBox::ICallbacks *pCallbacks );
// The main functions to operate the filtered combo box are here.
// This is the list of strings that is filtered into the dropdown combo box.
// flags is a combination of the SETSUGGESTIONS_ flags.
void SetSuggestions( CUtlVector<CString> &suggestions, int flags=SETSUGGESTIONS_SELECTFIRST|SETSUGGESTIONS_CALLBACK );
// Add a single suggestion (if it's unique).
void AddSuggestion( const CString &suggestion );
// This clears all items from the combo and its textbox.
void Clear();
// This will force the edit control text. It won't call OnTextChanged.
void ForceEditControlText( const char *pStr );
// This sets the main mode that the box runs in.
//
// If you pass true, then it will only ever call ICallbacks::OnTextChanged with values that are in your suggestions,
// and it does its best to autocomplete to those suggestions (so if the user types a partial string and closes
// the box, it will find the first possible substring match OR it will revert to the last valid suggestion it was on).
//
// If you pass false, then it will call OnTextChanged for anything that gets entered into the textbox. This is used
// for the entity properties targetname box, and the entity name changes right along as you type.
// When the user presses enter, it does NOT automatically select the first suggestion. They have to use the arrow keys for that.
void SetOnlyProvideSuggestions( bool bOnlyProvideSuggestions );
// These provide access to special behavior like font and color.
// Puts this string in the edit control and selects it in the combo box.
void SelectItem( const char *pStr );
// Returns the same value as the last call to OnTextChanged().
CString GetCurrentItem();
// Get/set the font in the edit control.
void SetEditControlFont( HFONT hFont );
HFONT GetEditControlFont() const;
// Get/set the color that the edit text is drawn in.
void SetEditControlTextColor( COLORREF clr );
COLORREF GetEditControlTextColor() const;
// General windows functions.
// Enable/disable the window.
bool IsWindowEnabled() const;
void EnableWindow( bool bEnable );
// Helper functions.
public:
// This takes the string the user has entered (pStringToMatch passed into GetItemsMatchingString)
// and returns true if pTestString matches it. It ignores underscores in both strings.
bool MatchString( const char *pStringToMatch, const char *pTestString );
// Does this string match one of the suggestions?
// Returns the suggestion index or -1.
int FindSuggestion( const char *pTest ) const;
// Returns the closest-matching suggestion (the first one that would appear
// in the autocomplete list) or the last known good suggestion.
CString GetBestSuggestion( const char *pTest );
void SubclassDlgItem(UINT nID, CWnd *pParent);
protected:
// Get the base font it's using.
CFont& GetNormalFont();
// Get/set the text in the edit control.
void SetEditControlText( const char *pText );
CString GetEditControlText() const;
DECLARE_MESSAGE_MAP()
bool m_bNotifyParent; // Whether we allow our parent to hook our notification messages.
// This is necessary because CControlBar-derived classes result in multiple
// message reflections unless we disable parent notification.
protected:
// Put all suggestions into the dropdown list.
void FillDropdownList( const char *pInitialSel, bool bEnableRedraw=true );
// CBN_ notification handlers.
virtual BOOL PreCreateWindow( CREATESTRUCT& cs );
BOOL OnDropDown();
BOOL OnSelEndOK();
BOOL OnCloseUp();
BOOL OnSelChange();
virtual BOOL OnEditChange();
afx_msg HBRUSH OnCtlColor(CDC *pDC, CWnd *pWnd, UINT nCtlColor);
void OnEnterKeyPressed( const char *pForceText );
void OnEscapeKeyPressed();
void DoTextChangedCallback( const char *pText );
// Gets the items matching the string and sorts the list alphabetically.
virtual void GetItemsMatchingString( const char *pStringToMatch, CUtlVector<CString> &matchingItems );
static int SortFn( const CString *pItem1, const CString *pItem2 );
virtual LRESULT DefWindowProc(
UINT message,
WPARAM wParam,
LPARAM lParam );
// Overrides for owner draw.
virtual void MeasureItem(LPMEASUREITEMSTRUCT pStruct);
virtual void DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct);
private:
void InternalSetEditControlFont( HFONT hFont, const char *pEditText, DWORD sel );
void CreateFonts();
bool InternalSelectItemByName( const char *pName );
private:
CUtlVector<CString> m_Suggestions;
HFONT m_hEditControlFont;
CFont m_NormalFont;
CFilteredComboBox::ICallbacks *m_pCallbacks;
bool m_bWasEditing;
DWORD m_dwTextColor;
bool m_bOnlyProvideSuggestions;
bool m_bInEnterKeyPressedHandler;
HFONT m_hQueuedFont;
bool m_bInSelChange;
// We go back here if they type text that we can't give a suggestion on and press enter (or lose focus).
CString m_LastTextChangedValue;
};
#endif // FILTERED_COMBO_BOX_H

141
hammer/HammerScene.cpp Normal file
View File

@@ -0,0 +1,141 @@
//====== Copyright © 1996-2005, Valve Corporation, All rights reserved. =======
//
// Purpose:
//
//=============================================================================
#include "stdafx.h"
#include "scriplib.h"
#include "choreoscene.h"
#include "iscenetokenprocessor.h"
#include "filesystem_tools.h"
//-----------------------------------------------------------------------------
// Purpose: Helper to scene module for parsing the .vcd file
//-----------------------------------------------------------------------------
class CSceneTokenProcessor : public ISceneTokenProcessor
{
public:
const char *CurrentToken( void );
bool GetToken( bool crossline );
bool TokenAvailable( void );
void Error( const char *fmt, ... );
};
//-----------------------------------------------------------------------------
// Purpose:
// Output : const
//-----------------------------------------------------------------------------
const char *CSceneTokenProcessor::CurrentToken( void )
{
return token;
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : crossline -
// Output : Returns true on success, false on failure.
//-----------------------------------------------------------------------------
bool CSceneTokenProcessor::GetToken( bool crossline )
{
return ::GetToken( crossline ) ? true : false;
}
//-----------------------------------------------------------------------------
// Purpose:
// Output : Returns true on success, false on failure.
//-----------------------------------------------------------------------------
bool CSceneTokenProcessor::TokenAvailable( void )
{
return ::TokenAvailable() ? true : false;
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : *fmt -
// ... -
//-----------------------------------------------------------------------------
void CSceneTokenProcessor::Error( const char *fmt, ... )
{
char string[ 2048 ];
va_list argptr;
va_start( argptr, fmt );
vsprintf( string, fmt, argptr );
va_end( argptr );
Warning( "%s", string );
}
static CSceneTokenProcessor g_TokenProcessor;
//-----------------------------------------------------------------------------
// Purpose: Normally implemented in cmdlib.cpp but we don't want that in Hammer.
//-----------------------------------------------------------------------------
char *ExpandPath (char *path)
{
static char fullpath[ 512 ];
g_pFullFileSystem->RelativePathToFullPath( path, "GAME", fullpath, sizeof( fullpath ) );
return fullpath;
}
//-----------------------------------------------------------------------------
// Purpose: Normally implemented in cmdlib.cpp but we don't want that in Hammer.
//-----------------------------------------------------------------------------
int LoadFile( const char *filename, void **bufferptr )
{
FileHandle_t f = g_pFullFileSystem->Open( filename, "rb" );
if ( FILESYSTEM_INVALID_HANDLE != f )
{
int length = g_pFullFileSystem->Size( f );
void *buffer = malloc (length+1);
((char *)buffer)[length] = 0;
g_pFullFileSystem->Read( buffer, length, f );
g_pFullFileSystem->Close (f);
*bufferptr = buffer;
return length;
}
else
{
*bufferptr = NULL;
return 0;
}
}
CChoreoScene* HammerLoadScene( const char *pFilename )
{
if ( g_pFullFileSystem->FileExists( pFilename ) )
{
LoadScriptFile( (char*)pFilename );
CChoreoScene *scene = ChoreoLoadScene( pFilename, NULL, &g_TokenProcessor, Msg );
return scene;
}
return NULL;
}
bool GetFirstSoundInScene( const char *pSceneFilename, char *pSoundName, int soundNameLen )
{
CChoreoScene *pScene = HammerLoadScene( pSceneFilename );
if ( !pScene )
return false;
for ( int i = 0; i < pScene->GetNumEvents(); i++ )
{
CChoreoEvent *e = pScene->GetEvent( i );
if ( !e || e->GetType() != CChoreoEvent::SPEAK )
continue;
const char *pParameters = e->GetParameters();
V_strncpy( pSoundName, pParameters, soundNameLen );
delete pScene;
return true;
}
delete pScene;
return false;
}

24
hammer/HammerScene.h Normal file
View File

@@ -0,0 +1,24 @@
//====== Copyright © 1996-2005, Valve Corporation, All rights reserved. =======
//
// Purpose:
//
//=============================================================================
#ifndef HAMMERSCENE_H
#define HAMMERSCENE_H
#ifdef _WIN32
#pragma once
#endif
class CChoreoScene;
// Load the specified vcd file.
CChoreoScene* HammerLoadScene( const char *pFilename );
// Load the VCD file and get the first sound in it.
bool GetFirstSoundInScene( const char *pSceneFilename, char *pSoundName, int soundNameLen );
#endif // HAMMERSCENE_H

205
hammer/HammerVGui.cpp Normal file
View File

@@ -0,0 +1,205 @@
#include "stdafx.h"
#include "hammer.h"
#include "hammervgui.h"
#include <vgui/IVGui.h>
#include <vgui/ISurface.h>
#include <vgui/ISystem.h>
#include "vgui/IInput.h"
#include "vgui_controls/EditablePanel.h"
#include <VGuiMatSurface/IMatSystemSurface.h>
#include <matsys_controls/matsyscontrols.h>
#include "material.h"
#include "vgui_controls/AnimationController.h"
#include "inputsystem/iinputsystem.h"
#include "VGuiWnd.h"
#include "toolutils/enginetools_int.h"
#include "toolframework/ienginetool.h"
#include "inputsystem/iinputstacksystem.h"
//-----------------------------------------------------------------------------
// Purpose: singleton accessor
//-----------------------------------------------------------------------------
// This window doesn't do anything other than tell CMatSystemSurface::CalculateMouseVisible to deem the mouse visible.
class CDummyPopupPanel : public vgui::Panel
{
public:
virtual void PaintBackground() {}
virtual void Paint() {}
};
static CHammerVGui s_HammerVGui;
CHammerVGui *HammerVGui()
{
return &s_HammerVGui;
}
CHammerVGui::CHammerVGui(void)
{
m_pActiveWindow = NULL;
m_hMainWindow = NULL;
m_pDummyPopup = NULL;
m_bCurrentDialogIsModal = false;
m_hHammerScheme = NULL;
m_hVguiInputContext = INPUT_CONTEXT_HANDLE_INVALID;
}
//-----------------------------------------------------------------------------
// Setup the base vgui panels
//-----------------------------------------------------------------------------
bool CHammerVGui::Init( HWND hWindow )
{
m_hMainWindow = hWindow;
if ( !APP()->IsFoundryMode() ) // We don't need to init most stuff in Foundry mode because the engine has already done it.
{
// initialize vgui_control interfaces
if (!vgui::VGui_InitInterfacesList( "HAMMER", &g_Factory, 1 ))
return false;
if ( !vgui::VGui_InitMatSysInterfacesList( "HAMMER", &g_Factory, 1 ) )
return false;
if ( !g_pMatSystemSurface )
return false;
// configuration settings
vgui::system()->SetUserConfigFile("hammer.vdf", "EXECUTABLE_PATH");
// Are we trapping input?
g_pMatSystemSurface->EnableWindowsMessages( true );
}
m_hVguiInputContext = g_pInputStackSystem->PushInputContext();
g_pMatSystemSurface->SetInputContext( m_hVguiInputContext );
// Need to be able to play sounds through vgui
// g_pMatSystemSurface->InstallPlaySoundFunc( VGui_PlaySound );
// load scheme
m_hHammerScheme = vgui::scheme()->LoadSchemeFromFile("//PLATFORM/Resource/SourceScheme.res", "Hammer");
if ( !m_hHammerScheme )
{
return false;
}
if ( !APP()->IsFoundryMode() ) // We don't need to init most stuff in Foundry mode because the engine has already done it.
{
// Start the App running
vgui::ivgui()->Start();
vgui::ivgui()->SetSleep(false);
// Create a popup window. This window doesn't do anything other than tell CMatSystemSurface::CalculateMouseVisible to deem the mouse visible.
m_pDummyPopup = new CDummyPopupPanel;
m_pDummyPopup->MakePopup( false, true );
m_pDummyPopup->SetVisible( true );
}
return true;
}
void CHammerVGui::SetFocus( CVGuiWnd *pVGuiWnd )
{
if ( pVGuiWnd == m_pActiveWindow )
return;
g_pInputSystem->PollInputState();
vgui::ivgui()->RunFrame();
g_pMatSystemSurface->SetAppDrivesInput( true );
g_pInputSystem->DetachFromWindow( );
// Disable mouse input on the previous panel so it doesn't get input the engine should get.
if ( m_pActiveWindow && m_pActiveWindow->GetMainPanel() )
{
m_pActiveWindow->GetMainPanel()->SetMouseInputEnabled( false );
}
if ( pVGuiWnd )
{
m_pActiveWindow = pVGuiWnd;
m_bCurrentDialogIsModal = m_pActiveWindow->IsModal();
Assert( pVGuiWnd->GetMainPanel() != NULL );
if ( pVGuiWnd->GetMainPanel() )
pVGuiWnd->GetMainPanel()->SetMouseInputEnabled( true );
g_pInputSystem->AttachToWindow( pVGuiWnd->GetParentWnd()->GetSafeHwnd() );
g_pMatSystemSurface->SetAppDrivesInput( !m_bCurrentDialogIsModal );
vgui::ivgui()->ActivateContext( pVGuiWnd->GetVGuiContext() );
// If this is a modal VGuiWnd (like the model browser), don't let the engine's message loop get called at all
// or else it'll screw up stuff - it'll give focus to other CVGuiWnds and the engine might drive
// some vgui stuff instead of the VGuiWnd message loop (in CVGuiWnd::WindowProcVGui).
if ( pVGuiWnd->IsModal() && enginetools )
::EnableWindow( (HWND)enginetools->GetEngineHwnd(), false );
}
else
{
if ( enginetools )
{
// We can't call m_pActiveWindow->IsModal here because it might be in its destructor (as with the model browser)
// and it's a virtual function.
if ( m_bCurrentDialogIsModal )
::EnableWindow( (HWND)enginetools->GetEngineHwnd(), true );
g_pInputSystem->AttachToWindow( enginetools->GetEngineHwnd() );
g_pMatSystemSurface->SetAppDrivesInput( true );
}
m_pActiveWindow = NULL;
vgui::ivgui()->ActivateContext( vgui::DEFAULT_VGUI_CONTEXT );
}
}
bool CHammerVGui::HasFocus( CVGuiWnd *pWnd )
{
return m_pActiveWindow == pWnd;
}
void CHammerVGui::Simulate()
{
// VPROF( "CHammerVGui::Simulate" );
if ( !IsInitialized() )
return;
g_pInputSystem->PollInputState();
vgui::ivgui()->RunFrame();
// run vgui animations
vgui::GetAnimationController()->UpdateAnimations( vgui::system()->GetCurrentTime() );
}
void CHammerVGui::Shutdown()
{
// Give panels a chance to settle so things
// Marked for deletion will actually get deleted
if ( !IsInitialized() )
return;
if ( m_pDummyPopup )
{
delete m_pDummyPopup;
m_pDummyPopup = NULL;
}
if ( m_hVguiInputContext != INPUT_CONTEXT_HANDLE_INVALID )
{
g_pMatSystemSurface->SetInputContext( NULL );
g_pInputStackSystem->PopInputContext();
m_hVguiInputContext = INPUT_CONTEXT_HANDLE_INVALID;
}
g_pInputSystem->PollInputState();
vgui::ivgui()->RunFrame();
// stop the App running
vgui::ivgui()->Stop();
}
CHammerVGui::~CHammerVGui(void)
{
}

56
hammer/HammerVGui.h Normal file
View File

@@ -0,0 +1,56 @@
//===== Copyright © 1996-2005, Valve Corporation, All rights reserved. ======//
//
// Purpose: Implements all the functions exported by the GameUI dll
//
// $NoKeywords: $
//===========================================================================//
#ifdef COMPILER_MSVC
#pragma once
#endif
#include "vgui/vgui.h"
#include "tier3/tier3.h"
namespace vgui
{
class Panel;
}
class IMatSystemSurface;
class CVGuiWnd;
FORWARD_DECLARE_HANDLE( InputContextHandle_t );
class CHammerVGui
{
public:
CHammerVGui(void);
~CHammerVGui(void);
bool Init( HWND hWindow );
void Simulate();
void Shutdown();
bool HasFocus( CVGuiWnd *pWnd );
void SetFocus( CVGuiWnd *pWnd );
bool IsInitialized() { return m_hMainWindow != NULL; };
vgui::HScheme GetHammerScheme() { return m_hHammerScheme; }
protected:
CVGuiWnd *m_pActiveWindow; // the VGUI window that has the focus
bool m_bCurrentDialogIsModal; // m_pActiveWindow->IsModal()
HWND m_hMainWindow;
vgui::Panel *m_pDummyPopup;
vgui::HScheme m_hHammerScheme;
InputContextHandle_t m_hVguiInputContext;
};
CHammerVGui *HammerVGui();

451
hammer/MapAxisHandle.cpp Normal file
View File

@@ -0,0 +1,451 @@
//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============//
//
// Purpose: A helper that represents the axis of rotation for a rotating entity.
// When selected, it exposes handles for the endpoints of the axis.
//
// It writes the axis as a keyvalue of the form:
//
// "x0 y0 z0, x1 y1 z1"
//
//=============================================================================//
#include "stdafx.h"
#include "Box3D.h"
#include "GlobalFunctions.h"
#include "fgdlib/HelperInfo.h"
#include "materialsystem/IMaterialSystem.h"
#include "materialsystem/IMesh.h"
#include "MainFrm.h" // For refreshing the object properties dialog
#include "MapDoc.h"
#include "MapAxisHandle.h"
#include "MapPointHandle.h"
#include "MapView2D.h"
#include "Material.h"
#include "Options.h"
#include "Render2D.h"
#include "Render3D.h"
#include "ToolManager.h"
#include "ToolAxisHandle.h"
// memdbgon must be the last include file in a .cpp file!!!
#include <tier0/memdbgon.h>
IMPLEMENT_MAPCLASS(CMapAxisHandle);
//-----------------------------------------------------------------------------
// Purpose: Factory function. Used for creating a CMapAxisHandle from a set
// of string parameters from the FGD file.
// Input : pInfo - Pointer to helper info class which gives us information
// about how to create the class.
// Output : Returns a pointer to the class, NULL if an error occurs.
//-----------------------------------------------------------------------------
CMapClass *CMapAxisHandle::Create(CHelperInfo *pHelperInfo, CMapEntity *pParent)
{
static char *pszDefaultKeyName = "axis";
const char *pszKey = pHelperInfo->GetParameter(0);
if (pszKey == NULL)
{
pszKey = pszDefaultKeyName;
}
CMapAxisHandle *pBox = new CMapAxisHandle(pszKey);
pBox->SetRenderColor(255, 255, 255);
return(pBox);
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : pfMins -
// pfMaxs -
//-----------------------------------------------------------------------------
CMapAxisHandle::CMapAxisHandle(void)
{
Initialize();
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : pfMins -
// pfMaxs -
//-----------------------------------------------------------------------------
CMapAxisHandle::CMapAxisHandle(const char *pszKey)
{
Initialize();
strcpy(m_szKeyName, pszKey);
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CMapAxisHandle::Initialize(void)
{
m_szKeyName[0] = '\0';
r = 255;
g = 255;
b = 255;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
CMapAxisHandle::~CMapAxisHandle(void)
{
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : bFullUpdate -
//-----------------------------------------------------------------------------
void CMapAxisHandle::CalcBounds(BOOL bFullUpdate)
{
CMapClass::CalcBounds(bFullUpdate);
//
// We don't affect our parent's 2D render bounds.
//
m_Render2DBox.ResetBounds();
//
// Calculate 3D culling box.
//
m_CullBox.ResetBounds();
for (int i = 0; i < 2; i++)
{
m_Point[i].CalcBounds(bFullUpdate);
Vector vecMins;
Vector vecMaxs;
m_Point[i].GetCullBox(vecMins, vecMaxs);
m_CullBox.UpdateBounds(vecMins, vecMaxs);
}
m_BoundingBox = m_CullBox;
}
//-----------------------------------------------------------------------------
// Purpose:
// Output : CMapClass
//-----------------------------------------------------------------------------
CMapClass *CMapAxisHandle::Copy(bool bUpdateDependencies)
{
CMapAxisHandle *pCopy = new CMapAxisHandle;
if (pCopy != NULL)
{
pCopy->CopyFrom(this, bUpdateDependencies);
}
return(pCopy);
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : pObject -
// Output : CMapClass
//-----------------------------------------------------------------------------
CMapClass *CMapAxisHandle::CopyFrom(CMapClass *pObject, bool bUpdateDependencies)
{
Assert(pObject->IsMapClass(MAPCLASS_TYPE(CMapAxisHandle)));
CMapAxisHandle *pFrom = (CMapAxisHandle *)pObject;
CMapClass::CopyFrom(pObject, bUpdateDependencies);
m_Point[0].CopyFrom(&pFrom->m_Point[0], bUpdateDependencies);
m_Point[1].CopyFrom(&pFrom->m_Point[1], bUpdateDependencies);
strcpy(m_szKeyName, pFrom->m_szKeyName);
return(this);
}
//-----------------------------------------------------------------------------
// Purpose: Gets the tool object for a given context data from HitTest2D.
//-----------------------------------------------------------------------------
CBaseTool *CMapAxisHandle::GetToolObject(int nHitData, bool bAttachObject)
{
// FIXME: ideally, we could use CToolPointHandle here, because all it does is move
// points around, but that would require some way for the CMapAxisHandle to know
// when the CMapPointHandle's position changes. This way the CToolAxisHandle can
// handle the notification. In general, we need a better system for building complex
// objects from simple ones and handling changes to the simple objects in the complex one.
//
// If we DID use a CToolPointHandle, we'd need to reconcile the status bar updates that
// are done in OnMouseMove2D, because points and axes cause different status bar text
// to be displayed as they are dragged around.
CToolAxisHandle *pTool = (CToolAxisHandle *)ToolManager()->GetToolForID(TOOL_AXIS_HANDLE);
if ( bAttachObject )
{
pTool->Attach(this, nHitData);
}
return pTool;
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : pView -
// point -
// nData -
// Output : Returns true on success, false on failure.
//-----------------------------------------------------------------------------
bool CMapAxisHandle::HitTest2D(CMapView2D *pView, const Vector2D &point, HitInfo_t &HitData)
{
if ( IsVisible() )
{
for (int i = 0; i < 2; i++)
{
if ( m_Point[i].HitTest2D(pView, point, HitData) )
{
HitData.pObject = this;
HitData.uData = i;
HitData.nDepth = 0; // map helpers have no real depth
return true;
}
}
}
return false;
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : pRender -
//-----------------------------------------------------------------------------
void CMapAxisHandle::Render2D(CRender2D *pRender)
{
SelectionState_t eState = GetSelectionState();
if (eState == SELECT_NONE)
return;
m_Point[0].Render2D(pRender);
m_Point[1].Render2D(pRender);
if (eState == SELECT_MODIFY)
{
pRender->PushRenderMode( RENDER_MODE_DOTTED );
pRender->SetDrawColor( GetRValue(Options.colors.clrSelection), GetGValue(Options.colors.clrSelection), GetBValue(Options.colors.clrSelection) );
}
else
{
pRender->PushRenderMode( RENDER_MODE_FLAT );
pRender->SetDrawColor( GetRValue(Options.colors.clrToolHandle), GetGValue(Options.colors.clrToolHandle), GetBValue(Options.colors.clrToolHandle) );
}
Vector vecOrigin1;
Vector vecOrigin2;
m_Point[0].GetOrigin(vecOrigin1);
m_Point[1].GetOrigin(vecOrigin2);
pRender->DrawLine(vecOrigin1, vecOrigin2);
pRender->PopRenderMode();
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : pRender -
//-----------------------------------------------------------------------------
void CMapAxisHandle::Render3D(CRender3D *pRender)
{
if (GetSelectionState() != SELECT_NONE)
{
for (int i = 0; i < 2; i++)
{
m_Point[i].Render3D(pRender);
}
Vector vec1;
Vector vec2;
m_Point[0].GetOrigin(vec1);
m_Point[1].GetOrigin(vec2);
pRender->SetDrawColor( 255, 255, 255 );
pRender->DrawLine(vec1, vec2);
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
int CMapAxisHandle::SerializeRMF(std::fstream &File, BOOL bRMF)
{
return(0);
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
int CMapAxisHandle::SerializeMAP(std::fstream &File, BOOL bRMF)
{
return(0);
}
//-----------------------------------------------------------------------------
// Purpose: Overridden to chain down to our endpoints, which are not children.
//-----------------------------------------------------------------------------
void CMapAxisHandle::SetOrigin(Vector &vecOrigin)
{
BaseClass::SetOrigin(vecOrigin);
m_Point[0].SetOrigin(vecOrigin);
m_Point[1].SetOrigin(vecOrigin);
}
//-----------------------------------------------------------------------------
// Purpose: Overridden to chain down to our endpoints, which are not children.
//-----------------------------------------------------------------------------
SelectionState_t CMapAxisHandle::SetSelectionState(SelectionState_t eSelectionState)
{
SelectionState_t eState = BaseClass::SetSelectionState(eSelectionState);
m_Point[0].SetSelectionState(eSelectionState);
m_Point[1].SetSelectionState(eSelectionState);
return eState;
}
//-----------------------------------------------------------------------------
// Purpose: Special version of set SelectionState to set the state in only one
// endpoint handle for dragging that handle.
//-----------------------------------------------------------------------------
SelectionState_t CMapAxisHandle::SetSelectionState(SelectionState_t eSelectionState, int nHandle)
{
SelectionState_t eState = BaseClass::SetSelectionState(eSelectionState);
m_Point[nHandle].SetSelectionState(eSelectionState);
return eState;
}
//-----------------------------------------------------------------------------
// Purpose: Overridden because origin helpers don't take the color of their
// parent entity.
// Input : red, green, blue -
//-----------------------------------------------------------------------------
void CMapAxisHandle::SetRenderColor(unsigned char red, unsigned char green, unsigned char blue)
{
}
//-----------------------------------------------------------------------------
// Purpose: Overridden because origin helpers don't take the color of their
// parent entity.
// Input : red, green, blue -
//-----------------------------------------------------------------------------
void CMapAxisHandle::SetRenderColor(color32 rgbColor)
{
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : szKey -
// szValue -
//-----------------------------------------------------------------------------
void CMapAxisHandle::OnParentKeyChanged(const char *szKey, const char *szValue)
{
if (!stricmp(szKey, m_szKeyName))
{
Vector vecOrigin1;
Vector vecOrigin2;
sscanf(szValue, "%f %f %f, %f %f %f", &vecOrigin1.x, &vecOrigin1.y, &vecOrigin1.z, &vecOrigin2.x, &vecOrigin2.y, &vecOrigin2.z);
m_Point[0].SetOrigin(vecOrigin1);
m_Point[1].SetOrigin(vecOrigin2);
CalcBounds();
}
}
//-----------------------------------------------------------------------------
// Purpose: Called by the axis tool to update the position of the endpoint.
//-----------------------------------------------------------------------------
void CMapAxisHandle::UpdateEndPoint(Vector &vecPos, int nPointIndex)
{
m_Point[nPointIndex].m_Origin = vecPos;
CalcBounds();
UpdateParentKey();
}
//-----------------------------------------------------------------------------
// Purpose: Overridden to transform our endpoints.
//-----------------------------------------------------------------------------
void CMapAxisHandle::DoTransform(const VMatrix &matrix)
{
BaseClass::DoTransform(matrix);
m_Point[0].Transform(matrix);
m_Point[1].Transform(matrix);
UpdateParentKey();
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CMapAxisHandle::UpdateParentKey(void)
{
if (m_szKeyName[0])
{
CMapEntity *pEntity = dynamic_cast <CMapEntity *> (m_pParent);
if (pEntity != NULL)
{
Vector vecOrigin1;
Vector vecOrigin2;
m_Point[0].GetOrigin(vecOrigin1);
m_Point[1].GetOrigin(vecOrigin2);
CalcBounds();
char szValue[KEYVALUE_MAX_VALUE_LENGTH];
sprintf(szValue, "%g %g %g, %g %g %g", (double)vecOrigin1.x, (double)vecOrigin1.y, (double)vecOrigin1.z, (double)vecOrigin2.x, (double)vecOrigin2.y, (double)vecOrigin2.z);
pEntity->NotifyChildKeyChanged(this, m_szKeyName, szValue);
}
}
}
//-----------------------------------------------------------------------------
// Purpose: Sets the keyvalue in our parent when we are added to the world.
// Input : pWorld -
//-----------------------------------------------------------------------------
void CMapAxisHandle::OnAddToWorld(CMapWorld *pWorld)
{
BaseClass::OnAddToWorld(pWorld);
UpdateParentKey();
}
//-----------------------------------------------------------------------------
// Purpose: Sets the keyvalue in our parent after the map is loaded.
// Input : pWorld -
//-----------------------------------------------------------------------------
void CMapAxisHandle::PostloadWorld(CMapWorld *pWorld)
{
BaseClass::PostloadWorld(pWorld);
UpdateParentKey();
}

129
hammer/MapAxisHandle.h Normal file
View File

@@ -0,0 +1,129 @@
//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
//=============================================================================//
#ifndef MAPAXISHANDLE_H
#define MAPAXISHANDLE_H
#ifdef _WIN32
#pragma once
#endif
#include "MapClass.h"
#include "MapPointHandle.h"
#include "ToolInterface.h"
#include "MapPointHandle.h"
#include "mapview.h"
class CHelperInfo;
class CRender2D;
class CRender3D;
class CToolAxisHandle;
#define MAX_KEYNAME_SIZE 32
class CMapAxisHandle : public CMapHelper
{
friend CToolAxisHandle;
public:
DECLARE_MAPCLASS(CMapAxisHandle, CMapHelper)
//
// Factory for building from a list of string parameters.
//
static CMapClass *Create(CHelperInfo *pInfo, CMapEntity *pParent);
//
// Construction/destruction:
//
CMapAxisHandle();
CMapAxisHandle(const char *pszKey);
~CMapAxisHandle();
inline void GetEndPoint(Vector &vecPos, int nPointIndex);
void UpdateEndPoint(Vector &vecPos, int nPointIndex);
inline int GetEndPointRadius();
//
// CMapClass implementation.
//
void CalcBounds(BOOL bFullUpdate = FALSE);
virtual CMapClass *Copy(bool bUpdateDependencies);
virtual CMapClass *CopyFrom(CMapClass *pFrom, bool bUpdateDependencies);
virtual void Render2D(CRender2D *pRender);
virtual int SerializeRMF(std::fstream &File, BOOL bRMF);
virtual int SerializeMAP(std::fstream &File, BOOL bRMF);
// Overridden to chain down to our endpoints, which are not children.
void SetOrigin(Vector &vecOrigin);
// Overridden to chain down to our endpoints, which are not children.
virtual SelectionState_t SetSelectionState(SelectionState_t eSelectionState);
// Overridden because axis helpers don't take the color of their parent entity.
virtual void SetRenderColor(unsigned char red, unsigned char green, unsigned char blue);
virtual void SetRenderColor(color32 rgbColor);
virtual bool HitTest2D(CMapView2D *pView, const Vector2D &point, HitInfo_t &HitData);
virtual CBaseTool *GetToolObject(int nHitData, bool bAttachObject);
virtual bool IsVisualElement(void) { return(false); } // Only visible if our parent is selected.
virtual bool IsClutter(void) const { return true; }
virtual bool CanBeCulledByCordon() const { return false; } // We don't hide unless our parent hides.
virtual const char* GetDescription() { return("Axis helper"); }
virtual void OnAddToWorld(CMapWorld *pWorld);
virtual void OnParentKeyChanged(const char *key, const char *value);
virtual void PostloadWorld(CMapWorld *pWorld);
virtual void Render3D(CRender3D *pRender);
protected:
SelectionState_t SetSelectionState(SelectionState_t eSelectionState, int nHandle);
void UpdateParentKey(void);
// Overriden to transform our endpoints, which are not children.
virtual void DoTransform(const VMatrix &matrix);
void Initialize(void);
CMapPointHandle m_Point[2]; // The two endpoints of the axis.
char m_szKeyName[MAX_KEYNAME_SIZE];
};
//-----------------------------------------------------------------------------
// Purpose: Returns the position of the given endpoint.
// Input : vecPos - Receives the position.
// nPointIndex - Endpoint index [0,1].
//-----------------------------------------------------------------------------
inline void CMapAxisHandle::GetEndPoint(Vector &vecPos, int nPointIndex)
{
m_Point[nPointIndex].GetOrigin(vecPos);
}
//-----------------------------------------------------------------------------
// Purpose: Returns the radius to use in rendering the endpoints.
//-----------------------------------------------------------------------------
inline int CMapAxisHandle::GetEndPointRadius()
{
return m_Point[0].GetRadius();
}
#endif // MAPAXISHANDLE_H

171
hammer/MapDiffDlg.cpp Normal file
View File

@@ -0,0 +1,171 @@
// MapDiffDlg.cpp : implementation file
//
#include "stdafx.h"
#include "GlobalFunctions.h"
#include "History.h"
#include "MainFrm.h"
#include "MapDiffDlg.h"
#include "MapDoc.h"
#include "MapEntity.h"
#include "MapSolid.h"
#include "MapView2D.h"
#include "MapWorld.h"
#include "ObjectProperties.h" // For ObjectProperties::RefreshData
#include "Options.h"
#include "ToolManager.h"
#include "VisGroup.h"
#include "hammer.h"
#include "MapOverlay.h"
#include "GameConfig.h"
// memdbgon must be the last include file in a .cpp file!!!
#include <tier0/memdbgon.h>
#include ".\mapdiffdlg.h"
CMapDiffDlg *s_pDlg = NULL;
CMapDoc *s_pCurrentMap = NULL;
// MapDiffDlg dialog
CMapDiffDlg::CMapDiffDlg(CWnd* pParent )
: CDialog(CMapDiffDlg::IDD, pParent)
{
m_bCheckSimilar = true;
}
void CMapDiffDlg::DoDataExchange(CDataExchange* pDX)
{
CDialog::DoDataExchange(pDX);
DDX_Check(pDX, IDC_SIMILARCHECK, m_bCheckSimilar);
DDX_Control(pDX, IDC_MAPNAME, m_mapName);
}
BEGIN_MESSAGE_MAP(CMapDiffDlg, CDialog)
ON_BN_CLICKED(IDC_SIMILARCHECK, OnBnClickedSimilarcheck)
ON_BN_CLICKED(IDC_MAPBROWSE, OnBnClickedMapbrowse)
ON_BN_CLICKED(IDOK, OnBnClickedOk)
ON_BN_CLICKED(IDCANCEL, OnBnClickedCancel)
ON_WM_DESTROY()
END_MESSAGE_MAP()
void CMapDiffDlg::MapDiff(CWnd *pwndParent, CMapDoc *pCurrentMapDoc)
{
if (!s_pDlg)
{
s_pDlg = new CMapDiffDlg;
s_pDlg->Create(IDD, pwndParent);
s_pDlg->ShowWindow(SW_SHOW);
s_pCurrentMap = pCurrentMapDoc;
}
}
// MapDiffDlg message handlers
void CMapDiffDlg::OnBnClickedSimilarcheck()
{
// TODO: Add your control notification handler code here
m_bCheckSimilar = !m_bCheckSimilar;
}
void CMapDiffDlg::OnBnClickedMapbrowse()
{
CString m_pszFilename;
// TODO: Add your control notification handler code here
static char szInitialDir[MAX_PATH] = "";
if (szInitialDir[0] == '\0')
{
strcpy(szInitialDir, g_pGameConfig->szMapDir);
}
// TODO: need to prevent (or handle) opening VMF files when using old map file formats
CFileDialog dlg(TRUE, NULL, NULL, OFN_LONGNAMES | OFN_HIDEREADONLY | OFN_NOCHANGEDIR, "Valve Map Files (*.vmf)|*.vmf|Valve Map Files Autosaves (*.vmf_autosave)|*.vmf_autosave|Worldcraft RMFs (*.rmf)|*.rmf|Worldcraft Maps (*.map)|*.map||");
dlg.m_ofn.lpstrInitialDir = szInitialDir;
int iRvl = dlg.DoModal();
if (iRvl == IDCANCEL)
{
return;
}
//
// Get the directory they browsed to for next time.
//
m_pszFilename = dlg.GetPathName();
m_mapName.SetWindowText( m_pszFilename );
}
void CMapDiffDlg::OnBnClickedOk()
{
// TODO: Add your control notification handler code here
OnOK();
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CMapDiffDlg::OnOK()
{
CString strFilename;
m_mapName.GetWindowText( strFilename );
CHammer *pApp = (CHammer*) AfxGetApp();
CMapDoc *pDoc = (CMapDoc*) pApp->pMapDocTemplate->OpenDocumentFile( strFilename );
CUtlVector <int> IDList;
const CMapObjectList *pChildren = pDoc->GetMapWorld()->GetChildren();
FOR_EACH_OBJ( *pChildren, pos )
{
int nID = pChildren->Element(pos)->GetID();
IDList.AddToTail( nID );
}
pDoc->OnCloseDocument();
CVisGroup *resultsVisGroup = NULL;
pChildren = s_pCurrentMap->GetMapWorld()->GetChildren();
int nTotalSimilarities = 0;
if ( m_bCheckSimilar )
{
FOR_EACH_OBJ( *pChildren, pos )
{
CMapClass *pChild = (CUtlReference< CMapClass >)pChildren->Element(pos) ;
int ID = pChild->GetID();
if ( IDList.Find( ID ) != -1 )
{
if ( resultsVisGroup == NULL )
{
resultsVisGroup = s_pCurrentMap->VisGroups_AddGroup( "Similar" );
nTotalSimilarities++;
}
pChild->AddVisGroup( resultsVisGroup );
}
}
}
if ( nTotalSimilarities > 0 )
{
GetMainWnd()->MessageBox( "Similarities were found and placed into the \"Similar\" visgroup.", "Map Similarities Found", MB_OK | MB_ICONEXCLAMATION);
}
s_pCurrentMap->VisGroups_UpdateAll();
DestroyWindow();
}
//-----------------------------------------------------------------------------
// Purpose: Called when our window is being destroyed.
//-----------------------------------------------------------------------------
void CMapDiffDlg::OnDestroy()
{
delete this;
s_pDlg = NULL;
s_pCurrentMap = NULL;
}
void CMapDiffDlg::OnBnClickedCancel()
{
// TODO: Add your control notification handler code here
DestroyWindow();
}

41
hammer/MapDiffDlg.h Normal file
View File

@@ -0,0 +1,41 @@
#ifndef MAPDIFFDLG_H
#define MAPDIFFDLG_H
#ifdef _WIN32
#pragma once
#endif
#include "mapdoc.h"
// MapDiffDlg dialog
class CMapDiffDlg : public CDialog
{
public:
static void MapDiff(CWnd *pwndParent, CMapDoc *p_CurrentMap);
private:
CMapDiffDlg(CWnd* pParent = NULL); // standard constructor
enum { IDD = IDD_DIFFMAP };
BOOL m_bCheckSimilar;
CEdit m_mapName;
protected:
virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support
virtual void OnOK();
afx_msg void OnDestroy();
DECLARE_MESSAGE_MAP()
public:
afx_msg void OnBnClickedSimilarcheck();
afx_msg void OnBnClickedMapbrowse();
afx_msg void OnBnClickedOk();
afx_msg void OnBnClickedCancel();
};
#endif //MAPDIFFDLG_H

36
hammer/MapHelper.h Normal file
View File

@@ -0,0 +1,36 @@
//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============//
//
// Purpose: Defines a base class for all helper objects. Helper objects are
// subordinate to their entity parents, and provide services such as
// enhanced presentation and manipulation of keyvalues for their parent
// entity.
//
// Like all children, helpers are transformed with their parent.
//
// $NoKeywords: $
//=============================================================================//
#ifndef MAPHELPER_H
#define MAPHELPER_H
#ifdef _WIN32
#pragma once
#endif
#include "MapClass.h"
class CMapHelper : public CMapClass
{
public:
//
// Serialization.
//
virtual bool ShouldSerialize(void) { return(false); }
virtual CMapClass *PrepareSelection(SelectMode_t eSelectMode);
};
#endif // MAPHELPER_H

310
hammer/ModelBrowser.cpp Normal file
View File

@@ -0,0 +1,310 @@
// ModelBrowser.cpp : implementation file
//
#include "stdafx.h"
#include "ModelBrowser.h"
#include "matsys_controls/mdlpicker.h"
#include "matsys_controls/baseassetpicker.h"
#include "matsys_controls/matsyscontrols.h"
#include "vgui_controls/TextEntry.h"
#include "vgui_controls/Splitter.h"
#include "vgui_controls/Button.h"
#include "KeyValues.h"
#include "vgui/KeyCode.h"
#include "texturesystem.h"
#include "HammerVGui.h"
static LPCTSTR pszIniSection = "Model Browser";
// CModelBrowser dialog
class CModelBrowserPanel : public vgui::EditablePanel
{
public:
CModelBrowserPanel( CModelBrowser *pBrowser, const char *panelName, vgui::HScheme hScheme ) :
vgui::EditablePanel( NULL, panelName, hScheme )
{
m_pBrowser = pBrowser;
}
virtual void OnSizeChanged(int newWide, int newTall)
{
// call Panel and not EditablePanel OnSizeChanged.
Panel::OnSizeChanged(newWide, newTall);
}
virtual void OnCommand( const char *pCommand )
{
if ( Q_strcmp( pCommand, "OK" ) == 0 )
{
m_pBrowser->EndDialog( IDOK );
}
else if ( Q_strcmp( pCommand, "Cancel" ) == 0 )
{
m_pBrowser->EndDialog( IDCANCEL );
}
}
virtual void OnKeyCodeTyped(vgui::KeyCode code)
{
vgui::EditablePanel::OnKeyCodeTyped( code );
if ( code == KEY_ENTER )
{
m_pBrowser->EndDialog( IDOK );
}
else if ( code == KEY_ESCAPE )
{
m_pBrowser->EndDialog( IDCANCEL );
}
}
virtual void OnMessage(const KeyValues *params, vgui::VPANEL ifromPanel)
{
vgui::EditablePanel::OnMessage( params, ifromPanel );
if ( Q_strcmp( params->GetName(), "MDLPreviewChanged" ) == 0 )
{
m_pBrowser->UpdateStatusLine();
}
else if ( Q_stricmp( params->GetName(), "AssetPickerFind" ) == 0 )
{
m_pBrowser->EndDialog( ID_FIND_ASSET );
}
}
CModelBrowser *m_pBrowser;
};
IMPLEMENT_DYNAMIC(CModelBrowser, CDialog)
CModelBrowser::CModelBrowser(CWnd* pParent /*=NULL*/)
: CDialog(CModelBrowser::IDD, pParent)
{
m_pPicker = new CMDLPicker( NULL );
m_pStatusLine = new vgui::TextEntry( NULL, "StatusLine" );
m_pButtonOK = new vgui::Button( NULL, "OpenButton", "OK" );
m_pButtonCancel = new vgui::Button( NULL, "CancelButton", "Cancel" );
}
CModelBrowser::~CModelBrowser()
{
delete m_pPicker;
delete m_pStatusLine;
delete m_pButtonOK;
delete m_pButtonCancel;
}
void CModelBrowser::SetUsedModelList( CUtlVector<AssetUsageInfo_t> &usedModels )
{
m_pPicker->SetUsedAssetList( usedModels );
}
void CModelBrowser::SetModelName( const char *pModelName )
{
char pszTempModelName[255];
strcpy( pszTempModelName, pModelName );
char * pszSelectedModel = strchr( pszTempModelName, '/' );
if( pszSelectedModel)
{
pszSelectedModel += 1;
Q_FixSlashes( pszSelectedModel, '\\' );
}
m_pPicker->SelectMDL( pModelName );
m_pPicker->SetInitialSelection( pszSelectedModel );
m_pStatusLine->SetText( pModelName );
}
void CModelBrowser::GetModelName( char *pModelName, int length )
{
m_pPicker->GetSelectedMDLName( pModelName, length );
Q_FixSlashes( pModelName, '/' );
}
void CModelBrowser::GetSkin( int &nSkin )
{
nSkin = m_pPicker->GetSelectedSkin();
}
void CModelBrowser::SetSkin( int nSkin )
{
m_pPicker->SelectSkin( nSkin );
}
void CModelBrowser::UpdateStatusLine()
{
char szModel[1024];
m_pPicker->GetSelectedMDLName( szModel, sizeof(szModel) );
m_pStatusLine->SetText( szModel );
/* MDLHandle_t hMDL = vgui::MDLCache()->FindMDL( szModel );
studiohdr_t *hdr = vgui::MDLCache()->GetStudioHdr( hMDL );
vgui::MDLCache()->Release( hMDL ); */
}
void CModelBrowser::SaveLoadSettings( bool bSave )
{
CString str;
CRect rect;
CWinApp *pApp = AfxGetApp();
if ( bSave )
{
GetWindowRect(rect);
str.Format("%d %d %d %d", rect.left, rect.top, rect.right, rect.bottom);
pApp->WriteProfileString(pszIniSection, "Position", str);
pApp->WriteProfileString(pszIniSection, "Filter", m_pPicker->GetFilter() );
}
else
{
str = pApp->GetProfileString(pszIniSection, "Position");
if (!str.IsEmpty())
{
sscanf(str, "%d %d %d %d", &rect.left, &rect.top, &rect.right, &rect.bottom);
MoveWindow(rect.left, rect.top, rect.right-rect.left, rect.bottom-rect.top, FALSE);
Resize();
}
str = pApp->GetProfileString(pszIniSection, "Filter");
if (!str.IsEmpty())
{
m_pPicker->SetFilter( str );
}
}
}
void CModelBrowser::DoDataExchange(CDataExchange* pDX)
{
CDialog::DoDataExchange(pDX);
}
void CModelBrowser::Resize()
{
// reposition controls
CRect rect;
GetClientRect(&rect);
m_VGuiWindow.MoveWindow( rect );
m_pPicker->SetBounds( 0,0, rect.Width(), rect.Height() - 32 );
m_pButtonCancel->SetPos( 8, rect.Height() - 30 );
m_pButtonOK->SetPos( 84, rect.Height() - 30 );
m_pStatusLine->SetBounds( 160, rect.Height() - 30, max( 100, rect.Width() - 166 ), 24 );
}
void CModelBrowser::OnSize(UINT nType, int cx, int cy)
{
if (nType == SIZE_MINIMIZED || !IsWindow(m_VGuiWindow.m_hWnd) )
{
CDialog::OnSize(nType, cx, cy);
return;
}
Resize();
CDialog::OnSize(nType, cx, cy);
}
BEGIN_MESSAGE_MAP(CModelBrowser, CDialog)
ON_WM_SIZE()
ON_WM_DESTROY()
ON_WM_ERASEBKGND()
END_MESSAGE_MAP()
BOOL CModelBrowser::PreTranslateMessage( MSG* pMsg )
{
// don't filter dialog message
return CWnd::PreTranslateMessage( pMsg );
}
BOOL CModelBrowser::OnInitDialog()
{
CDialog::OnInitDialog();
m_VGuiWindow.Create( NULL, _T("ModelViewer"), WS_VISIBLE|WS_CHILD, CRect(0,0,100,100), this, 1001);
vgui::EditablePanel *pMainPanel = new CModelBrowserPanel( this, "ModelBrowerPanel", HammerVGui()->GetHammerScheme() );
m_VGuiWindow.SetParentWindow( &m_VGuiWindow );
m_VGuiWindow.SetMainPanel( pMainPanel );
pMainPanel->MakePopup( false, false );
m_VGuiWindow.SetRepaintInterval( 75 );
m_pPicker->SetParent( pMainPanel );
m_pPicker->AddActionSignalTarget( pMainPanel );
m_pButtonOK->SetParent( pMainPanel );
m_pButtonOK->AddActionSignalTarget( pMainPanel );
m_pButtonOK->SetCommand( "OK" );
m_pButtonCancel->SetParent( pMainPanel );
m_pButtonCancel->AddActionSignalTarget( pMainPanel );
m_pButtonCancel->SetCommand( "Cancel" );
m_pStatusLine->SetParent( pMainPanel );
m_pStatusLine->SetEditable( false );
SaveLoadSettings( false ); // load
m_pPicker->Activate();
return TRUE;
}
void CModelBrowser::OnDestroy()
{
SaveLoadSettings( true ); // save
// model browser destoys our default cube map, reload it
g_Textures.RebindDefaultCubeMap();
CDialog::OnDestroy();
}
void CModelBrowser::Show()
{
if (m_pPicker)
{
m_pPicker->SetVisible( true );
}
if (m_pStatusLine)
m_pStatusLine->SetVisible( true );
if (m_pButtonOK)
m_pButtonOK->SetVisible( true );
if (m_pButtonCancel)
m_pButtonCancel->SetVisible( true );
}
void CModelBrowser::Hide()
{
if (m_pPicker)
m_pPicker->SetVisible( false );
if (m_pStatusLine)
m_pStatusLine->SetVisible( false );
if (m_pButtonOK)
m_pButtonOK->SetVisible( false );
if (m_pButtonCancel)
m_pButtonCancel->SetVisible( false );
}
BOOL CModelBrowser::OnEraseBkgnd(CDC* pDC)
{
return TRUE;
}

69
hammer/ModelBrowser.h Normal file
View File

@@ -0,0 +1,69 @@
#pragma once
#include "resource.h"
#include "utlvector.h"
#include "VGuiWnd.h"
#include "matsys_controls\baseassetpicker.h"
namespace vgui
{
class TextEntry;
class Splitter;
class Button;
}
class CModelBrowserPanel;
class CMDLPicker;
#define ID_FIND_ASSET 100
class CModelBrowser : public CDialog
{
DECLARE_DYNAMIC(CModelBrowser)
public:
CModelBrowser(CWnd* pParent = NULL); // standard constructor
virtual ~CModelBrowser();
void SetUsedModelList( CUtlVector<AssetUsageInfo_t> &usedModels );
void SetModelName( const char *pModelName );
void GetModelName( char *pModelName, int length );
void GetSkin( int &nSkin );
void SetSkin( int nSkin );
// Dialog Data
enum { IDD = IDD_MODEL_BROWSER };
protected:
virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support
virtual BOOL PreTranslateMessage( MSG* pMsg );
DECLARE_MESSAGE_MAP()
public:
afx_msg void OnSize(UINT nType, int cx, int cy);
afx_msg void OnDestroy();
afx_msg BOOL OnEraseBkgnd(CDC* pDC);
virtual BOOL OnInitDialog();
void UpdateStatusLine();
void SaveLoadSettings( bool bSave );
void Resize( void );
CVGuiPanelWnd m_VGuiWindow;
CMDLPicker *m_pPicker;
vgui::Button *m_pButtonOK;
vgui::Button *m_pButtonCancel;
vgui::TextEntry *m_pStatusLine;
void Show();
void Hide();
};

176
hammer/NewVisGroupDlg.cpp Normal file
View File

@@ -0,0 +1,176 @@
//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============//
//
// Purpose: A dialog that is invoked when a new visgroup is created.
// It lets the user pick an existing visgroup or create a new one.
//
//=============================================================================//
#include "stdafx.h"
#include "MapDoc.h"
#include "NewVisGroupDlg.h"
#include "hammer.h"
// memdbgon must be the last include file in a .cpp file!!!
#include <tier0/memdbgon.h>
static const unsigned int g_uSelChangeMsg = ::RegisterWindowMessage(GROUPLIST_MSG_SEL_CHANGE);
static BOOL s_bLastHideObjects = TRUE;
BEGIN_MESSAGE_MAP(CNewVisGroupDlg, CDialog)
//{{AFX_MSG_MAP(CNewVisGroupDlg)
ON_REGISTERED_MESSAGE(g_uSelChangeMsg, OnSelChangeGroupList)
ON_COMMAND(IDC_PLACE_IN_EXISTING_VISGROUP, OnPlaceInExistingVisGroup)
ON_COMMAND(IDC_CREATE_NEW_VISGROUP, OnCreateNewVisGroup)
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
//-----------------------------------------------------------------------------
// Purpose:
// Input : pParent -
//-----------------------------------------------------------------------------
CNewVisGroupDlg::CNewVisGroupDlg(CString &str, CWnd *pParent)
: CDialog(CNewVisGroupDlg::IDD, pParent)
{
m_pPickedVisGroup = NULL;
//{{AFX_DATA_INIT(CNewVisGroupDlg)
m_strName = str;
//}}AFX_DATA_INIT
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : pDX -
//-----------------------------------------------------------------------------
void CNewVisGroupDlg::DoDataExchange(CDataExchange* pDX)
{
CDialog::DoDataExchange(pDX);
//{{AFX_DATA_MAP(CNewVisGroupDlg)
DDX_Check(pDX, IDC_REMOVE_FROM_ALL, m_bRemoveFromOtherGroups);
DDX_Check(pDX, IDC_HIDE_OBJECTS, m_bHideObjects);
DDX_Text(pDX, IDC_VISGROUP_NAME, m_strName);
//}}AFX_DATA_MAP
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
BOOL CNewVisGroupDlg::OnInitDialog(void)
{
m_bHideObjects = s_bLastHideObjects;
CDialog::OnInitDialog();
CButton *pButton = (CButton *)GetDlgItem(IDC_CREATE_NEW_VISGROUP);
pButton->SetCheck(1);
m_cGroupList.SubclassDlgItem(IDC_GROUP_LIST, this);
UpdateGroupList();
CEdit *pEdit = (CEdit *)GetDlgItem(IDC_GROUP_LIST);
pEdit->EnableWindow(FALSE);
return TRUE;
}
//-----------------------------------------------------------------------------
// Purpose: Returns the visgroup name that was entered in the dialog.
//-----------------------------------------------------------------------------
void CNewVisGroupDlg::GetName(CString &str)
{
str = m_strName;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CNewVisGroupDlg::OnOK()
{
CDialog::OnOK();
s_bLastHideObjects = m_bHideObjects;
}
//-----------------------------------------------------------------------------
// Purpose: Switches the mode of the dialog to pick an existing visgroup rather than
// create a new one.
//-----------------------------------------------------------------------------
void CNewVisGroupDlg::OnPlaceInExistingVisGroup()
{
CEdit *pEdit = (CEdit *)GetDlgItem(IDC_VISGROUP_NAME);
pEdit->EnableWindow(FALSE);
pEdit = (CEdit *)GetDlgItem(IDC_GROUP_LIST);
pEdit->EnableWindow(TRUE);
}
//-----------------------------------------------------------------------------
// Purpose: Switches the mode of the dialog to create a new visgroup rather than
// pick an existing one.
//-----------------------------------------------------------------------------
void CNewVisGroupDlg::OnCreateNewVisGroup()
{
CEdit *pEdit = (CEdit *)GetDlgItem(IDC_VISGROUP_NAME);
pEdit->EnableWindow(TRUE);
pEdit = (CEdit *)GetDlgItem(IDC_GROUP_LIST);
pEdit->EnableWindow(FALSE);
m_pPickedVisGroup = NULL;
}
//-----------------------------------------------------------------------------
// Purpose: Handles selection change in the visgroup list.
//-----------------------------------------------------------------------------
LRESULT CNewVisGroupDlg::OnSelChangeGroupList(WPARAM wParam, LPARAM lParam)
{
m_pPickedVisGroup = m_cGroupList.GetSelectedVisGroup();
return 0;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CNewVisGroupDlg::UpdateGroupList(void)
{
m_cGroupList.SetRedraw(false);
m_cGroupList.DeleteAllItems();
CMapDoc *pDoc = CMapDoc::GetActiveMapDoc();
if (pDoc != NULL)
{
int nCount = pDoc->VisGroups_GetRootCount();
for (int i = 0; i < nCount; i++)
{
CVisGroup *pGroup = pDoc->VisGroups_GetRootVisGroup(i);
if (stricmp(pGroup->GetName(), "Auto") != 0)
{
m_cGroupList.AddVisGroup(pGroup);
}
}
}
m_cGroupList.ExpandAll();
m_cGroupList.SetRedraw(true);
m_cGroupList.Invalidate();
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
CVisGroup *CNewVisGroupDlg::GetPickedVisGroup(void)
{
return m_pPickedVisGroup;
}

73
hammer/NewVisGroupDlg.h Normal file
View File

@@ -0,0 +1,73 @@
//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============//
//
// Purpose: A dialog that is invoked when a new visgroup is created.
//
//=============================================================================//
#ifndef NEWVISGROUPDLG_H
#define NEWVISGROUPDLG_H
#ifdef _WIN32
#pragma once
#endif
#include "resource.h"
#include "GroupList.h"
class CNewVisGroupDlg : public CDialog
{
public:
CNewVisGroupDlg(CString &str, CWnd *pParent = NULL);
void GetName(CString &str);
CVisGroup *GetPickedVisGroup(void);
bool GetRemoveFromOtherGroups(void);
bool GetHideObjectsOption(void);
//{{AFX_DATA(CNewVisGroupDlg)
enum { IDD = IDD_NEW_VISGROUP };
//}}AFX_DATA
// ClassWizard generated virtual function overrides
//{{AFX_VIRTUAL(CNewVisGroupDlg)
protected:
virtual void DoDataExchange(CDataExchange *pDX);
virtual BOOL OnInitDialog(void);
//}}AFX_VIRTUAL
protected:
void UpdateGroupList(void);
// Generated message map functions
//{{AFX_MSG(CNewVisGroupDlg)
virtual void OnOK();
LRESULT OnSelChangeGroupList(WPARAM wParam, LPARAM lParam);
void OnCreateNewVisGroup();
void OnPlaceInExistingVisGroup();
//}}AFX_MSG
CGroupList m_cGroupList;
CVisGroup *m_pPickedVisGroup;
BOOL m_bRemoveFromOtherGroups;
BOOL m_bHideObjects;
CString m_strName;
DECLARE_MESSAGE_MAP()
};
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
inline bool CNewVisGroupDlg::GetRemoveFromOtherGroups(void)
{
return m_bRemoveFromOtherGroups == TRUE;
}
inline bool CNewVisGroupDlg::GetHideObjectsOption()
{
return (m_bHideObjects != FALSE);
}
#endif // NEWVISGROUPDLG_H

342
hammer/OP_Model.cpp Normal file
View File

@@ -0,0 +1,342 @@
//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ====
//
// Purpose:
//
//=============================================================================
#include "stdafx.h"
#include "hammer.h"
#include "MapEntity.h"
#include "MapStudioModel.h"
#include "OP_Model.h"
#include "ObjectProperties.h"
#include "mapdoc.h"
#include "options.h"
// memdbgon must be the last include file in a .cpp file!!!
#include <tier0/memdbgon.h>
#pragma warning( disable : 4355 )
IMPLEMENT_DYNCREATE(COP_Model, CObjectPage)
BEGIN_MESSAGE_MAP(COP_Model, CObjectPage)
//{{AFX_MSG_MAP(COP_Model)
ON_WM_HSCROLL()
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
const int FRAME_SCROLLBAR_RANGE = 1000;
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
COP_Model::COP_Model() : CObjectPage(COP_Model::IDD), m_ComboSequence( this )
{
//{{AFX_DATA_INIT(COP_Model)
//}}AFX_DATA_INIT
m_pEditObjectRuntimeClass = RUNTIME_CLASS(editCEditGameClass);
m_ComboSequence.SetOnlyProvideSuggestions( true );
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
COP_Model::~COP_Model()
{
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : pDX -
//-----------------------------------------------------------------------------
void COP_Model::DoDataExchange(CDataExchange* pDX)
{
CObjectPage::DoDataExchange(pDX);
//{{AFX_DATA_MAP(COP_Model)
DDX_Control(pDX, IDC_SEQUENCE, m_ComboSequence);
DDX_Control(pDX, IDC_FRAME_SCROLLBAR, m_ScrollBarFrame);
//}}AFX_DATA_MAP
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : Mode -
// pData -
//-----------------------------------------------------------------------------
void COP_Model::UpdateData( int Mode, PVOID pData, bool bCanEdit )
{
__super::UpdateData( Mode, pData, bCanEdit );
if (!IsWindow(m_hWnd) || !pData)
{
return;
}
if (Mode == LoadFirstData)
{
m_ComboSequence.Clear();
CMapStudioModel *pModel = GetModelHelper();
if ( pModel )
{
// If they were on a previous animation, remember it and we'll set the combo box to that after
// we tell it the list of suggestions.
char txt[512];
txt[0] = 0;
int iSequence = pModel->GetSequence();
if ( iSequence )
pModel->GetSequenceName( iSequence, txt );
// Set the list of suggestions.
CUtlVector<CString> suggestions;
int nCount = pModel->GetSequenceCount();
for ( int i = 0; i < nCount; i++ )
{
char szName[MAX_PATH];
pModel->GetSequenceName(i, szName);
suggestions.AddToTail( szName );
}
m_ComboSequence.SetSuggestions( suggestions, 0 );
m_ComboSequence.SetCurSel( iSequence );
}
// Reset the scroll bar
InitScrollRange();
}
else if (Mode == LoadData)
{
Assert(false);
}
SetReadOnly( !m_bCanEdit );
}
BOOL COP_Model::OnSetActive()
{
m_bOldAnimatedModels = Options.view3d.bAnimateModels;
Options.view3d.bAnimateModels = true;
CMapStudioModel *pModel = GetModelHelper();
if ( pModel )
{
m_nOldSequence = pModel->GetSequence();
}
CMapDoc *pDoc = CMapDoc::GetActiveMapDoc();
if ( pDoc )
{
pDoc->UpdateAllViews( MAPVIEW_UPDATE_ANIMATION|MAPVIEW_OPTIONS_CHANGED );
}
return CObjectPage::OnSetActive();
}
BOOL COP_Model::OnKillActive()
{
Options.view3d.bAnimateModels = m_bOldAnimatedModels;
CMapDoc *pDoc = CMapDoc::GetActiveMapDoc();
if ( pDoc )
{
pDoc->UpdateAllViews( MAPVIEW_UPDATE_ANIMATION|MAPVIEW_OPTIONS_CHANGED );
}
return CObjectPage::OnKillActive();
}
//-----------------------------------------------------------------------------
// Purpose:
// Output : Returns true on success, false on failure.
//-----------------------------------------------------------------------------
bool COP_Model::SaveData( SaveData_Reason_t reason )
{
if (!IsWindow(m_hWnd))
{
return false;
}
// If we've closed the dialog or changed focus, reset the model now
if ( reason == SAVEDATA_SELECTION_CHANGED || reason == SAVEDATA_CLOSE )
{
CMapStudioModel *pModel = GetModelHelper();
if (pModel != NULL)
{
pModel->SetSequence( m_nOldSequence );
pModel->SetFrame( 0 );
CMapDoc *pDoc = CMapDoc::GetActiveMapDoc();
if ( pDoc )
{
pDoc->UpdateAllViews( MAPVIEW_UPDATE_ANIMATION );
}
}
}
return true;
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : pszClass -
//-----------------------------------------------------------------------------
void COP_Model::UpdateForClass(LPCTSTR pszClass)
{
if (!IsWindow(m_hWnd))
{
return;
}
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : flFrame -
//-----------------------------------------------------------------------------
void COP_Model::UpdateFrameText( int nFrame)
{
char szFrame[40];
sprintf(szFrame, "%d", nFrame);
GetDlgItem(IDC_FRAME_TEXT)->SetWindowText(szFrame);
}
//-----------------------------------------------------------------------------
// Purpose:
// Output : Returns TRUE on success, FALSE on failure.
//-----------------------------------------------------------------------------
BOOL COP_Model::OnInitDialog()
{
CObjectPage::OnInitDialog();
InitScrollRange();
return TRUE;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void COP_Model::InitScrollRange( void )
{
// Set the frame number scrollbar range
int nMaxRange = FRAME_SCROLLBAR_RANGE;
CMapStudioModel *pModel = GetModelHelper();
if (pModel != NULL)
{
nMaxRange = pModel->GetMaxFrame();
}
// Setup the bar
m_ScrollBarFrame.SetRange( 0, nMaxRange );
m_ScrollBarFrame.SetPos( 0 );
// Start at the zeroth frame
UpdateFrameText( 0 );
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : nSBCode -
// nPos -
// pScrollBar -
//-----------------------------------------------------------------------------
void COP_Model::OnHScroll(UINT nSBCode, UINT nPos, CScrollBar *pScrollBar)
{
if (pScrollBar == (CScrollBar *)&m_ScrollBarFrame)
{
if ( nSBCode == SB_ENDSCROLL )
return;
CMapStudioModel *pModel = GetModelHelper();
if (pModel != NULL)
{
pModel->SetFrame( nPos );
UpdateFrameText( nPos );
Options.view3d.bAnimateModels = false; // Pause animations while we're scrubbing
CMapDoc *pDoc = CMapDoc::GetActiveMapDoc();
if ( pDoc )
{
pDoc->UpdateAllViews( MAPVIEW_UPDATE_ANIMATION|MAPVIEW_OPTIONS_CHANGED );
}
}
}
CPropertyPage::OnHScroll(nSBCode, nPos, pScrollBar);
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void COP_Model::OnTextChanged( const char *pText )
{
CMapStudioModel *pModel = GetModelHelper();
if (pModel != NULL)
{
int iSequence = pModel->GetSequenceIndex( pText );
if ( iSequence != -1 )
pModel->SetSequence( iSequence );
pModel->SetFrame( 0 );
InitScrollRange();
Options.view3d.bAnimateModels = true; // They've changed sequences, so allow animation again
CMapDoc *pDoc = CMapDoc::GetActiveMapDoc();
if ( pDoc )
{
pDoc->UpdateAllViews( MAPVIEW_UPDATE_ANIMATION|MAPVIEW_OPTIONS_CHANGED );
}
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
CMapStudioModel *COP_Model::GetModelHelper(void)
{
if ( m_pObjectList->Count() == 0 )
return NULL;
CMapClass *pObject = (CUtlReference< CMapClass >)m_pObjectList->Element( 0 );
if (pObject != NULL)
{
CMapEntity *pEntity = dynamic_cast <CMapEntity *>(pObject);
if (pEntity != NULL)
{
CMapStudioModel *pModel = pEntity->GetChildOfType((CMapStudioModel *)NULL);
return pModel;
}
}
return NULL;
}
//-----------------------------------------------------------------------------
// Purpose: sets the controls to be read only
// Input : bReadOnly - indicates if the controls should be read only
//-----------------------------------------------------------------------------
void COP_Model::SetReadOnly( bool bReadOnly )
{
m_ComboSequence.EnableWindow( bReadOnly ? FALSE : TRUE );
m_ScrollBarFrame.EnableWindow( bReadOnly ? FALSE : TRUE );
}

80
hammer/OP_Model.h Normal file
View File

@@ -0,0 +1,80 @@
//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
//=============================================================================//
#ifndef OP_MODEL_H
#define OP_MODEL_H
#ifdef _WIN32
#pragma once
#endif
#include "ObjectPage.h"
#include "FilteredComboBox.h"
class GDclass;
class CMapStudioModel;
class COP_Model : public CObjectPage, public CFilteredComboBox::ICallbacks
{
friend class CFilteredModelSequenceComboBox;
DECLARE_DYNCREATE(COP_Model)
public:
COP_Model();
~COP_Model();
virtual bool SaveData( SaveData_Reason_t reason );
virtual void UpdateData( int Mode, PVOID pData, bool bCanEdit );
void UpdateForClass(LPCTSTR pszClass);
void OnSelChangeSequence( int iSequence );
// Implementation of CFilteredComboBox::ICallbacks.
virtual void OnTextChanged( const char *pText );
// Variables.
GDclass *pObjClass;
//{{AFX_DATA(COP_Model)
enum { IDD = IDD_OBJPAGE_MODEL };
CFilteredComboBox m_ComboSequence;
CSliderCtrl m_ScrollBarFrame;
// DO NOT EDIT what you see in these blocks of generated code !
//}}AFX_DATA
// ClassWizard generate virtual function overrides
//{{AFX_VIRTUAL(COP_Model)
protected:
virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support
//}}AFX_VIRTUAL
private:
void SetReadOnly( bool bReadOnly );
protected:
CMapStudioModel *GetModelHelper(void);
void UpdateFrameText(int nFrame);
void InitScrollRange( void );
// Generated message map functions
//{{AFX_MSG(COP_Model)
virtual BOOL OnInitDialog();
virtual BOOL OnKillActive();
virtual BOOL OnSetActive();
afx_msg void OnHScroll(UINT nSBCode, UINT nPos, CScrollBar *pScrollBar);
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
BOOL m_bOldAnimatedModels;
int m_nOldSequence;
};
#endif // OP_MODEL_H

76
hammer/ObjectPage.cpp Normal file
View File

@@ -0,0 +1,76 @@
//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//=============================================================================//
#include "stdafx.h"
#include "hammer.h"
#include "ObjectPage.h"
#include "GlobalFunctions.h"
#include "ObjectProperties.h"
//
// Used to indicate multiselect of entities with different keyvalues.
//
char *CObjectPage::VALUE_DIFFERENT_STRING = "(different)";
//
// Set while we are changing the page layout.
//
BOOL CObjectPage::s_bRESTRUCTURING = FALSE;
IMPLEMENT_DYNCREATE(CObjectPage, CPropertyPage)
//-----------------------------------------------------------------------------
// Purpose: stores whether or not this page can be updated
// Input : Mode - unused
// pData - unused
// bCanEdit - the edit state
//-----------------------------------------------------------------------------
void CObjectPage::UpdateData( int Mode, PVOID pData, bool bCanEdit )
{
m_bCanEdit = bCanEdit;
}
//-----------------------------------------------------------------------------
// Purpose: Called when we become the active page.
//-----------------------------------------------------------------------------
BOOL CObjectPage::OnSetActive(void)
{
//VPROF_BUDGET( "CObjectPage::OnSetActive", "Object Properties" );
if (CObjectPage::s_bRESTRUCTURING || !GetActiveWorld())
{
return CPropertyPage::OnSetActive();
}
CObjectProperties *pParent = (CObjectProperties *)GetParent();
pParent->UpdateAnchors( this );
if (m_bFirstTimeActive)
{
m_bFirstTimeActive = false;
pParent->LoadDataForPages(pParent->GetPageIndex(this));
}
return CPropertyPage::OnSetActive();
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
PVOID CObjectPage::GetEditObject()
{
//VPROF_BUDGET( "CObjectPage::GetEditObject", "Object Properties" );
return ((CObjectProperties*) GetParent())->GetEditObject(GetEditObjectRuntimeClass());
}

17
hammer/PopupMenus.h Normal file
View File

@@ -0,0 +1,17 @@
//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
//=============================================================================//
#ifndef POPUPMENUS_H
#define POPUPMENUS_H
#ifdef _WIN32
#pragma once
#endif
#define IDM_POPUP_POINT_HANDLE 7
#endif // POPUPMENUS_H

1620
hammer/Render.cpp Normal file

File diff suppressed because it is too large Load Diff

319
hammer/Render.h Normal file
View File

@@ -0,0 +1,319 @@
//===== Copyright © 1996-2005, Valve Corporation, All rights reserved. ======//
//
// Purpose:
//
// $NoKeywords: $
//===========================================================================//
#ifndef RENDER_H
#define RENDER_H
#pragma once
#include "Color.h"
#include "utlstack.h"
#include "hammer_mathlib.h"
#include "MaterialSystem\imesh.h"
#include "shaderapi/ishaderapi.h"
class IMaterial;
struct DrawModelInfo_t;
class CCoreDispInfo;
class CMapView;
class CCamera;
class CMapAtom;
class IEditorTexture;
class CMapClass;
class CMapInstance;
typedef unsigned short MDLHandle_t;
#define CAMERA_FRONT_PLANE_DISTANCE 8.0f
#define CAMERA_HORIZONTAL_FOV 90.0f
//
// Colors for selected faces and edges. Kinda hacky; should probably be elsewhere.
//
#define SELECT_FACE_RED 220
#define SELECT_FACE_GREEN 0
#define SELECT_FACE_BLUE 0
#define SELECT_EDGE_RED 255
#define SELECT_EDGE_GREEN 255
#define SELECT_EDGE_BLUE 0
inline void SelectFaceColor( Color &pColor )
{
pColor[0] = SELECT_FACE_RED;
pColor[1] = SELECT_FACE_GREEN;
pColor[2] = SELECT_FACE_BLUE;
}
inline void SelectEdgeColor( Color &pColor )
{
pColor[0] = SELECT_EDGE_RED;
pColor[1] = SELECT_EDGE_GREEN;
pColor[2] = SELECT_EDGE_BLUE;
}
inline void InstanceColor( Color &pColor, bool bSelected )
{
if ( bSelected )
{
pColor[ 0 ] = 192;
pColor[ 1 ] = 128;
pColor[ 2 ] = 0;
pColor[ 3 ] = 192;
}
else
{
pColor[ 0 ] = 128;
pColor[ 1 ] = 128;
pColor[ 2 ] = 0;
pColor[ 3 ] = 192;
}
}
enum EditorRenderMode_t
{
RENDER_MODE_NONE = 0, // dont render anything
RENDER_MODE_EXTERN, // other system is using material system
RENDER_MODE_DEFAULT, // select default material
RENDER_MODE_CURRENT, // the current render mode
RENDER_MODE_WIREFRAME, // wire frame mode
RENDER_MODE_FLAT, // flat solid colors
RENDER_MODE_FLAT_NOZ, // flat solid colors, ignore Z
RENDER_MODE_FLAT_NOCULL, // flat solid colors, no backface culling
RENDER_MODE_DOTTED, // flat colored dotted, ignore Z
RENDER_MODE_TRANSLUCENT_FLAT,
RENDER_MODE_TEXTURED,
RENDER_MODE_LIGHTMAP_GRID,
RENDER_MODE_SELECTION_OVERLAY,
RENDER_MODE_SMOOTHING_GROUP,
RENDER_MODE_TEXTURED_SHADED,
RENDER_MODE_LIGHT_PREVIEW2,
RENDER_MODE_LIGHT_PREVIEW_RAYTRACED,
RENDER_MODE_INSTANCE_OVERLAY,
};
enum InstanceRenderingState_t
{
INSTANCE_STATE_OFF, // normal rendering
INSTANCE_STATE_ON, // will be tinted as an instance
INSTANCE_STACE_SELECTED // will be tinted as a selected instance
};
typedef struct SInstanceState
{
CMapInstance *m_pInstanceClass; // the func_instance entity
Vector m_InstanceOrigin; // the origin offset of instance rendering
QAngle m_InstanceAngles; // the rotation of the instance rendering
VMatrix m_InstanceMatrix; // matrix of the origin and rotation of rendering
VMatrix m_InstanceRenderMatrix; // matrix of the current camera transform
bool m_bIsEditable;
CMapInstance *m_pTopInstanceClass;
} TInstanceState;
//#define STENCIL_AS_CALLS 1
class CRender
{
public:
CRender(void);
virtual ~CRender(void);
enum
{ TEXT_SINGLELINE = 0x1, // put all of the text on one line
TEXT_MULTILINE = 0x2, // the text is written on multiple lines
TEXT_JUSTIFY_BOTTOM = 0x4, // default
TEXT_JUSTIFY_TOP = 0x8,
TEXT_JUSTIFY_RIGHT = 0x10, // default
TEXT_JUSTIFY_LEFT = 0x20,
TEXT_JUSTIFY_HORZ_CENTER = 0x40,
TEXT_JUSTIFY_VERT_CENTER = 0x80,
TEXT_CLEAR_BACKGROUND = 0x100
}; // clear the background behind the text
enum
{ HANDLE_NONE = 0,
HANDLE_SQUARE,
HANDLE_CIRCLE,
HANDLE_DIAMOND,
HANDLE_CROSS
};
// map view setup
virtual bool SetView( CMapView *pView );
CMapView *GetView() { return m_pView; }
CCamera *GetCamera();// { return m_pView->GetCamera(); }
bool IsActiveView();
// begin/end single render frame, sets up camera etc
virtual void StartRenderFrame( bool bRenderingOverEngine );
virtual void EndRenderFrame();
int GetRenderFrame() { return m_nFrameCount; }
// switch rendering to client space coordinates (horz,vert,ignore)
// render is in world space mode by default
bool BeginClientSpace(void);
void EndClientSpace(void);
bool IsInClientSpace() { return m_bIsClientSpace; }
void BeginLocalTransfrom( const VMatrix &matrix, bool MultiplyCurrent = false );
void EndLocalTransfrom();
bool IsInLocalTransformMode();
void GetLocalTranform( VMatrix &matrix );
void SetTextColor( unsigned char r, unsigned char g, unsigned char b, unsigned char a = 255 );
void SetDrawColor( unsigned char r, unsigned char g, unsigned char b );
void SetDrawColor( const Color &color );
void GetDrawColor( Color &color );
void SetHandleColor( unsigned char r, unsigned char g, unsigned char b );
void SetHandleStyle( int size, int type );
void SetDefaultRenderMode(EditorRenderMode_t eRenderMode);
void BindTexture(IEditorTexture *pTexture);
void BindMaterial( IMaterial *pMaterial );
virtual void SetRenderMode( EditorRenderMode_t eRenderMode, bool force = false);
inline EditorRenderMode_t GetCurrentRenderMode() { return m_eCurrentRenderMode; }
inline EditorRenderMode_t GetDefaultRenderMode() { return m_eDefaultRenderMode; }
void PushRenderMode( EditorRenderMode_t eRenderMode );
void PopRenderMode();
// drawing primitives
void DrawPoint( const Vector &vPoint );
void DrawLine( const Vector &vStart, const Vector &vEnd );
virtual void DrawBox( const Vector &vMins, const Vector &vMaxs, bool bFill = false );
void DrawBoxExt( const Vector &vCenter, float extend, bool bFill = false );
void DrawSphere( const Vector &vCenter, int nRadius );
void DrawCircle( const Vector &vCenter, const Vector &vNormal, float flRadius, int nSegments );
void DrawPolyLine( int nPoints, const Vector *Points );
void DrawText( const char *text, int x, int y, int nFlags ); // Uses pixel coordinates
void DrawText( const char *text, const Vector2D &vPos, int nOffsetX, int nOffsetY, int nFlags ); // Uses "world" coordinates
void DrawHandle( const Vector &vPoint, const Vector2D *vOffset = NULL );
void DrawHandles( int nPoints, const Vector *Points );
void DrawArrow( Vector const &vStart, Vector const &vEnd );
void DrawPlane( const Vector &p0, const Vector &p1, const Vector &p2, const Vector &p3, bool bFill = false );
// client space helper functions:
void DrawFilledRect( Vector2D& pt1, Vector2D& pt2, unsigned char *pColor, bool bBorder );
// drawing complex objects
void DrawModel( DrawModelInfo_t* pInfo, matrix3x4_t *pBoneToWorld, const Vector &vOrigin, float fAlpha = 1, bool bWireframe = false, const Color &color = Color( 255, 255, 255, 255 ) );
void DrawDisplacement( CCoreDispInfo *pDisp );
void DrawCollisionModel( MDLHandle_t mdlHandle, const VMatrix &mViewMatrix );
//
// helper funtions
//
void TransformPoint( Vector2D &vClient, const Vector& vWorld );
void TransformNormal( Vector2D &vClient, const Vector& vWorld );
void GetViewForward( Vector &ViewForward ) const;
void GetViewRight( Vector &ViewRight ) const;
void GetViewUp( Vector &ViewUp ) const;
void PrepareInstanceStencil( void );
void DrawInstanceStencil( void );
void PushInstanceRendering( InstanceRenderingState_t State );
void PopInstanceRendering( void );
void SetInstanceRendering( InstanceRenderingState_t State );
void SetInstanceRendering( bool InstanceRendering ) { m_bInstanceRendering = InstanceRendering; }
virtual void PushInstanceData( CMapInstance *pInstanceClass, Vector &InstanceOrigin, QAngle &InstanceAngles );
virtual void PopInstanceData( void );
bool GetInstanceRendering( void ) { return m_bInstanceRendering; }
CMapInstance *GetInstanceClass( void ) { return m_CurrentInstanceState.m_pInstanceClass; }
void GetInstanceMatrix( VMatrix &Matrix ) { Matrix = m_CurrentInstanceState.m_InstanceMatrix; }
Vector GetInstanceOrigin( void ) { return m_CurrentInstanceState.m_InstanceOrigin; }
QAngle GetInstanceAngle( void ) { return m_CurrentInstanceState.m_InstanceAngles; }
void TransformInstanceVector( Vector &In, Vector &Out ) { m_CurrentInstanceState.m_InstanceMatrix.V3Mul( In, Out ); }
void RotateInstanceVector( Vector &In, Vector &Out ) { VectorRotate( In, m_CurrentInstanceState.m_InstanceMatrix.As3x4(), Out ); }
void TransformInstanceAABB( Vector &InMins, Vector &InMaxs, Vector &OutMins, Vector &OutMaxs ) { TransformAABB( m_CurrentInstanceState.m_InstanceMatrix.As3x4(), InMins, InMaxs, OutMins, OutMaxs ); }
protected:
bool GetRequiredMaterial( const char *pName, IMaterial* &pMaterial );
void UpdateStudioRenderConfig( bool bFlat, bool bWireframe );
// client space helper functions:
void DrawCross( Vector2D& pt1, Vector2D& pt2, unsigned char *pColor );
void DrawCircle( Vector2D &vCenter, float fRadius, int nSegments, unsigned char *pColor );
void DrawRect( Vector2D& pt1, Vector2D& pt2, unsigned char *pColor );
protected:
CMapView *m_pView;
unsigned long m_DefaultFont;
bool m_bIsClientSpace;
bool m_bIsLocalTransform;
CUtlVector< VMatrix > m_LocalMatrix;
VMatrix m_OrthoMatrix;
// Are we rendering on top of the engine's view? If so, we avoid certain view setup things and we avoid drawing world geometry.
bool m_bRenderingOverEngine;
// Meshbuilder used for drawing
IMesh* m_pMesh;
CMeshBuilder meshBuilder;
// colors
Color m_DrawColor; // current draw/fill color
Color m_TextColor; // current text color
Color m_HandleColor; // current text color
// handle styles
int m_nHandleSize;
int m_nHandleType;
// frame count
int m_nFrameCount; // increases each setup camera
bool m_bIsRendering;
bool m_bIsRenderingIntoVGUI;
// materials
IMaterial* m_pCurrentMaterial; // The currently bound material
IMaterial* m_pBoundMaterial; // a material given from external caller
int m_nDecalMode; // 0 or 1
IMaterial* m_pWireframe[2]; // default wireframe material
IMaterial* m_pFlat[2]; // default flat material
IMaterial* m_pDotted[2]; // default dotted material
IMaterial* m_pFlatNoZ[2]; // default flat material, ignore Z
IMaterial* m_pFlatNoCull[2]; // default flat material, no backface cull
IMaterial* m_pTranslucentFlat[2]; // default translucent flat material
IMaterial* m_pLightmapGrid[2]; // default lightmap grid material
IMaterial* m_pSelectionOverlay[2]; // for selecting actual textures
// render modes
EditorRenderMode_t m_eCurrentRenderMode; // Current render mode setting - Wireframe, flat, or textured.
EditorRenderMode_t m_eDefaultRenderMode; // Default render mode - Wireframe, flat, or textured.
CUtlStack<EditorRenderMode_t> m_RenderModeStack;
// instance
int m_nInstanceCount; // increases each time an instance is drawn regardless of view
bool m_bInstanceRendering; // if true, we are rendering an instance
VMatrix m_CurrentMatrix; // matrix of the current transforms
int m_InstanceSelectionDepth;
TInstanceState m_CurrentInstanceState; // the current instance state ( with current transforms )
CUtlVector< TInstanceState > m_InstanceState; // the instance state stack
#ifndef STENCIL_AS_CALLS
ShaderStencilState_t m_ShaderStencilState;
#endif // STENCIL_AS_CALLS
int m_nNumInstancesRendered; // number of instances rendered that impacted the stencil buffer
CUtlVector< InstanceRenderingState_t > m_InstanceRenderingState; // the instance rendering state stack
};
#endif // RENDER_H

95
hammer/RenderUtils.cpp Normal file
View File

@@ -0,0 +1,95 @@
//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//=============================================================================//
#include "stdafx.h"
#include "Render2D.h"
#include "RenderUtils.h"
#include "mapview2d.h"
#include "toolinterface.h"
//-----------------------------------------------------------------------------
// Purpose: Draws the measurements of a brush in the 2D view.
// Input : pRender -
// Mins -
// Maxs -
// nFlags -
//-----------------------------------------------------------------------------
void DrawBoundsText(CRender2D *pRender, const Vector &Mins, const Vector &Maxs, int nFlags)
{
CMapView2D *pView = (CMapView2D*) pRender->GetView();
// Calculate the solid's extents along our 2D view axes.
Vector Extents = Maxs - Mins;
Vector Center = (Mins + Maxs ) * 0.5f;
for ( int i=0; i<3;i++ )
Extents[i] = fabs(Extents[i]);
// Transform the solids mins and maxs to 2D view space. These are used
// for placing the text in the view.
Vector2D projMins, projMaxs, projCenter;
pRender->TransformPoint( projMins, Mins );
pRender->TransformPoint( projMaxs, Maxs );
pRender->TransformPoint( projCenter, Center );
if( projMins.x > projMaxs.x )
{
V_swap( projMins.x, projMaxs.x );
}
if( projMins.y > projMaxs.y )
{
V_swap( projMins.y, projMaxs.y );
}
//
// display the extents of this brush
//
char extentText[30];
int nTextX, nTextY;
int nTextFlags;
pRender->SetTextColor( 255, 255, 255 );
// horz
sprintf( extentText, "%.1f", Extents[pView->axHorz] );
nTextFlags = CRender2D::TEXT_JUSTIFY_HORZ_CENTER;
nTextX = projCenter.x;
if ( nFlags & DBT_TOP )
{
nTextY = projMins.y - (HANDLE_RADIUS*3);
nTextFlags |= CRender2D::TEXT_JUSTIFY_TOP;
}
else
{
nTextY = projMaxs.y + (HANDLE_RADIUS*3);
nTextFlags |= CRender2D::TEXT_JUSTIFY_BOTTOM;
}
pRender->DrawText( extentText, nTextX, nTextY, nTextFlags );
// vert
sprintf( extentText, "%.1f", Extents[pView->axVert] );
nTextFlags = CRender2D::TEXT_JUSTIFY_VERT_CENTER;
nTextY = projCenter.y;
if ( nFlags & DBT_LEFT )
{
nTextX = projMins.x - (HANDLE_RADIUS*3);
nTextFlags |= CRender2D::TEXT_JUSTIFY_LEFT;
}
else
{
nTextX = projMaxs.x + (HANDLE_RADIUS*3);
nTextFlags |= CRender2D::TEXT_JUSTIFY_RIGHT;
}
pRender->DrawText( extentText, nTextX, nTextY, nTextFlags );
}

30
hammer/RenderUtils.h Normal file
View File

@@ -0,0 +1,30 @@
//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//=============================================================================//
#ifndef RENDERUTILS_H
#define RENDERUTILS_H
#ifdef _WIN32
#pragma once
#endif
class CRender2D;
//
// Flags for DrawBoundsText
//
#define DBT_TOP 0x1
#define DBT_BOTTOM 0x2
#define DBT_LEFT 0x4
#define DBT_RIGHT 0x8
#define DBT_BACK 0x10
void DrawBoundsText(CRender2D *pRender, const Vector &Mins, const Vector &Maxs, int nFlags);
#endif // RENDERUTILS_H

200
hammer/ScenePreviewDlg.cpp Normal file
View File

@@ -0,0 +1,200 @@
//====== Copyright © 1996-2005, Valve Corporation, All rights reserved. =======
//
// Purpose:
//
//=============================================================================
#include "stdafx.h"
#include "hammer.h"
#include "ScenePreviewDlg.h"
#include "choreoscene.h"
#include "soundsystem.h"
// memdbgon must be the last include file in a .cpp file!!!
#include <tier0/memdbgon.h>
#define WM_SCENEPREVIEW_IDLE (WM_USER+1)
BEGIN_MESSAGE_MAP(CScenePreviewDlg, CDialog)
//{{AFX_MSG_MAP(CScenePreviewDlg)
ON_BN_CLICKED(IDCANCEL, OnCancel)
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
CScenePreviewDlg::CScenePreviewDlg( CChoreoScene *pScene, const char *pFilename, CWnd* pParent /*=NULL*/ )
: CDialog(CScenePreviewDlg::IDD, pParent)
{
//{{AFX_DATA_INIT(CScenePreviewDlg)
m_pScene = pScene;
m_iLastEventPlayed = -2; // Don't do anything yet.
m_hExitThreadEvent = NULL;
m_hIdleEventHandledEvent = NULL;
m_hIdleThread = NULL;
V_strncpy( m_SceneFilename, pFilename, sizeof( m_SceneFilename ) );
}
CScenePreviewDlg::~CScenePreviewDlg()
{
EndThread();
delete m_pScene;
}
void CScenePreviewDlg::EndThread()
{
if ( m_hIdleThread )
{
SetEvent( m_hExitThreadEvent );
WaitForSingleObject( m_hIdleThread, INFINITE );
CloseHandle( m_hIdleThread );
CloseHandle( m_hExitThreadEvent );
CloseHandle( m_hIdleEventHandledEvent );
m_hIdleThread = m_hExitThreadEvent = m_hIdleEventHandledEvent = NULL;
}
}
BOOL CScenePreviewDlg::OnInitDialog()
{
CDialog::OnInitDialog();
// Setup the control showing the scene name.
CString str;
GetDlgItemText( IDC_SCENE_NAME, str );
str = str + " " + m_SceneFilename;
SetDlgItemText( IDC_SCENE_NAME, str );
CString strNone;
strNone.LoadString( IDS_NONE );
SetDlgItemText( IDC_CURRENT_SOUND, strNone );
SetDlgItemText( IDC_NEXT_SOUND, strNone );
m_iLastEventPlayed = -1;
m_flStartTime = Plat_FloatTime();
// Start on the first event..
for ( int i = 0; i < m_pScene->GetNumEvents(); i++ )
{
CChoreoEvent *e = m_pScene->GetEvent( i );
if ( e && e->GetType() == CChoreoEvent::SPEAK )
{
m_flStartTime -= e->GetStartTime();
break;
}
}
// Create our idle thread.
m_hExitThreadEvent = CreateEvent( NULL, FALSE, FALSE, NULL );
m_hIdleEventHandledEvent = CreateEvent( NULL, FALSE, FALSE, NULL );
m_hIdleThread = CreateThread( NULL, 0, &CScenePreviewDlg::StaticIdleThread, this, 0, NULL );
return TRUE;
}
DWORD CScenePreviewDlg::StaticIdleThread( LPVOID pParameter )
{
return ((CScenePreviewDlg*)pParameter)->IdleThread();
}
DWORD CScenePreviewDlg::IdleThread()
{
HANDLE handles[2] = {m_hExitThreadEvent, m_hIdleEventHandledEvent};
while ( 1 )
{
// Send the event to the window.
PostMessage( WM_SCENEPREVIEW_IDLE, 0, 0 );
DWORD ret = WaitForMultipleObjects( ARRAYSIZE( handles ), handles, false, INFINITE );
if ( ret == WAIT_OBJECT_0 || ret == WAIT_TIMEOUT )
return 0;
Sleep( 100 ); // Only handle idle 10x/sec.
}
}
void CScenePreviewDlg::OnIdle()
{
double flElapsed = Plat_FloatTime() - m_flStartTime;
// Find the next sound to play.
int iLastSound = -1;
for ( int i = 0; i < m_pScene->GetNumEvents(); i++ )
{
CChoreoEvent *e = m_pScene->GetEvent( i );
if ( !e || e->GetType() != CChoreoEvent::SPEAK )
continue;
if ( flElapsed > e->GetStartTime() )
iLastSound = i;
}
// Is there another sound to play in here?
if ( iLastSound >= 0 && iLastSound != m_iLastEventPlayed )
{
m_iLastEventPlayed = iLastSound;
// Play the current sound.
CChoreoEvent *e = m_pScene->GetEvent( iLastSound );
const char *pSoundName = e->GetParameters();
SoundType_t soundType;
int nIndex;
if ( g_Sounds.FindSoundByName( pSoundName, &soundType, &nIndex ) )
{
bool bRet = g_Sounds.Play( soundType, nIndex );
CString curSound = pSoundName;
if ( !bRet )
{
CString strErrorPlaying;
strErrorPlaying.LoadString( IDS_ERROR_PLAYING );
curSound += " " + strErrorPlaying;
}
SetDlgItemText( IDC_CURRENT_SOUND, curSound );
}
// Find the next sound event.
CString str;
str.LoadString( IDS_NONE );
for ( int i=iLastSound+1; i < m_pScene->GetNumEvents(); i++ )
{
CChoreoEvent *e = m_pScene->GetEvent( i );
if ( e && e->GetType() == CChoreoEvent::SPEAK )
{
str = e->GetParameters();
break;
}
}
SetDlgItemText( IDC_NEXT_SOUND, str );
}
}
LRESULT CScenePreviewDlg::DefWindowProc( UINT message, WPARAM wParam, LPARAM lParam )
{
// We handle the enter key specifically because the default combo box behavior is to
// reset the text and all this stuff we don't want.
if ( message == WM_SCENEPREVIEW_IDLE )
{
OnIdle();
SetEvent( m_hIdleEventHandledEvent );
return 0;
}
return CDialog::DefWindowProc( message, wParam, lParam );
}
void CScenePreviewDlg::OnCancel(void)
{
g_Sounds.StopSound();
EndThread();
EndDialog( 0 );
}

59
hammer/ScenePreviewDlg.h Normal file
View File

@@ -0,0 +1,59 @@
//====== Copyright © 1996-2005, Valve Corporation, All rights reserved. =======
//
// Purpose:
//
//=============================================================================
#ifndef SCENEPREVIEWDLG_H
#define SCENEPREVIEWDLG_H
#ifdef _WIN32
#pragma once
#endif
class CChoreoScene;
class CScenePreviewDlg : public CDialog
{
// Construction
public:
// Note: the dialog now owns the scene and it'll delete it when it goes away.
CScenePreviewDlg( CChoreoScene *pScene, const char *pSceneFilename, CWnd* pParent = NULL ); // standard constructor
virtual ~CScenePreviewDlg();
// Dialog Data
//{{AFX_DATA(CSoundBrowser)
DECLARE_MESSAGE_MAP()
enum { IDD = IDD_SCENE_PREVIEW };
protected:
BOOL OnInitDialog();
virtual LRESULT DefWindowProc( UINT message, WPARAM wParam, LPARAM lParam );
virtual void OnCancel(void);
private:
static DWORD WINAPI StaticIdleThread( LPVOID pParameter );
DWORD IdleThread();
void OnIdle();
void EndThread();
private:
CChoreoScene *m_pScene;
HANDLE m_hExitThreadEvent;
HANDLE m_hIdleEventHandledEvent;
HANDLE m_hIdleThread;
int m_iLastEventPlayed; // Last sound event we handled.
double m_flStartTime; // When we started playing the scene.
char m_SceneFilename[MAX_PATH];
};
#endif // SCENEPREVIEWDLG_H

599
hammer/SearchReplaceDlg.cpp Normal file
View File

@@ -0,0 +1,599 @@
//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//=============================================================================//
#include "stdafx.h"
#include "History.h"
#include "GlobalFunctions.h"
#include "MapDoc.h"
#include "MapWorld.h"
#include "SearchReplaceDlg.h"
#include "hammer.h"
#include "Selection.h"
// memdbgon must be the last include file in a .cpp file!!!
#include <tier0/memdbgon.h>
//
// Context data for a FindFirstObject/FindNextObject session.
//
struct FindObject_t
{
//
// Where to look: in the world or in the selection set.
//
FindReplaceIn_t eFindIn;
CMapWorld *pWorld;
EnumChildrenPos_t WorldPos; // A position in the world tree for world searches.
CMapObjectRefList SelectionList; // A copy of the selection list for selection only searches.
int nSelectionIndex; // The index into the selection list for iterating the selection list.
//
// What to look for.
//
CString strFindText;
bool bVisiblesOnly;
bool bCaseSensitive;
bool bWholeWord;
};
CMapClass *FindNextObject(FindObject_t &FindObject);
bool FindCheck(CMapClass *pObject, FindObject_t &FindObject);
//-----------------------------------------------------------------------------
// Purpose: Returns true if the string matches the search criteria, false if not.
// Input : pszString - String to check.
// FindObject - Search criteria, including string to search for.
//-----------------------------------------------------------------------------
bool MatchString(const char *pszString, FindObject_t &FindObject)
{
if (FindObject.bWholeWord)
{
if (FindObject.bCaseSensitive)
{
return (!strcmp(pszString, FindObject.strFindText));
}
return (!stricmp(pszString, FindObject.strFindText));
}
if (FindObject.bCaseSensitive)
{
return (strstr(pszString, FindObject.strFindText) != NULL);
}
return (Q_stristr(pszString, FindObject.strFindText) != NULL);
}
//-----------------------------------------------------------------------------
// Purpose: Returns true if the string matches the search criteria, false if not.
// Input : pszIn -
// pszOut - String to check.
// FindObject - Search criteria, including string to search for.
//-----------------------------------------------------------------------------
bool ReplaceString(char *pszOut, const char *pszIn, FindObject_t &FindObject, const char *pszReplace)
{
//
// Whole matches are simple, just strcpy the replacement string into the out buffer.
//
if (FindObject.bWholeWord)
{
if (FindObject.bCaseSensitive && (!strcmp(pszIn, FindObject.strFindText)))
{
strcpy(pszOut, pszReplace);
return true;
}
if (!stricmp(pszIn, FindObject.strFindText))
{
strcpy(pszOut, pszReplace);
return true;
}
}
//
// Partial matches are a little tougher.
//
const char *pszStart = NULL;
if (FindObject.bCaseSensitive)
{
pszStart = strstr(pszIn, FindObject.strFindText);
}
else
{
pszStart = Q_stristr(pszIn, FindObject.strFindText);
}
if (pszStart != NULL)
{
int nOffset = pszStart - pszIn;
strncpy(pszOut, pszIn, nOffset);
pszOut += nOffset;
pszIn += nOffset + strlen(FindObject.strFindText);
strcpy(pszOut, pszReplace);
pszOut += strlen(pszReplace);
strcpy(pszOut, pszIn);
return true;
}
return false;
}
//-----------------------------------------------------------------------------
// Purpose: Begins a Find or Find/Replace operation.
//-----------------------------------------------------------------------------
CMapClass *FindFirstObject(FindObject_t &FindObject)
{
CMapClass *pObject = NULL;
if (FindObject.eFindIn == FindInWorld)
{
// Search the entire world.
pObject = FindObject.pWorld->GetFirstDescendent(FindObject.WorldPos);
}
else
{
// Search the selection only.
if (FindObject.SelectionList.Count())
{
pObject = FindObject.SelectionList.Element(0);
FindObject.nSelectionIndex = 1;
}
}
if (!pObject)
{
return NULL;
}
if (FindCheck(pObject, FindObject))
{
return pObject;
}
return FindNextObject(FindObject);
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : pObject -
//-----------------------------------------------------------------------------
CMapClass *FindNextObject(FindObject_t &FindObject)
{
while (true)
{
CMapClass *pObject = NULL;
if (FindObject.eFindIn == FindInWorld)
{
// Search the entire world.
pObject = FindObject.pWorld->GetNextDescendent(FindObject.WorldPos);
}
else
{
// Search the selection only.
if (FindObject.nSelectionIndex < FindObject.SelectionList.Count())
{
pObject = FindObject.SelectionList.Element(FindObject.nSelectionIndex);
FindObject.nSelectionIndex++;
}
}
if ((!pObject) || FindCheck(pObject, FindObject))
{
return pObject;
}
}
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : pObject -
// FindObject -
// Output : Returns true if the object matches the search criteria, false if not.
//-----------------------------------------------------------------------------
bool FindCheck(CMapClass *pObject, FindObject_t &FindObject)
{
CMapEntity *pEntity = dynamic_cast <CMapEntity *>(pObject);
if (!pEntity)
{
return false;
}
if (FindObject.bVisiblesOnly && !pObject->IsVisible())
{
return false;
}
//
// Search keyvalues.
//
for ( int i=pEntity->GetFirstKeyValue(); i != pEntity->GetInvalidKeyValue(); i=pEntity->GetNextKeyValue( i ) )
{
const char *pszValue = pEntity->GetKeyValue(i);
if (pszValue && MatchString(pszValue, FindObject))
{
return true;
}
}
//
// Search connections.
//
int nConnCount = pEntity->Connections_GetCount();
for (int i = 0; i < nConnCount; i++)
{
CEntityConnection *pConn = pEntity->Connections_Get(i);
if (pConn)
{
if (MatchString(pConn->GetTargetName(), FindObject) ||
MatchString(pConn->GetParam(), FindObject))
{
return true;
}
}
}
return false;
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : pLastFound -
// FindObject -
// pszReplaceText -
// Output : Returns the number of occurrences of the find text that were replaced.
//-----------------------------------------------------------------------------
int FindReplace(CMapEntity *pEntity, FindObject_t &FindObject, const char *pszReplace)
{
int nReplacedCount = 0;
//
// Replace keyvalues.
//
for ( int i=pEntity->GetFirstKeyValue(); i != pEntity->GetInvalidKeyValue(); i=pEntity->GetNextKeyValue( i ) )
{
const char *pszValue = pEntity->GetKeyValue(i);
char szNewValue[MAX_PATH];
if (pszValue && ReplaceString(szNewValue, pszValue, FindObject, pszReplace))
{
const char *pszKey = pEntity->GetKey(i);
if (pszKey)
{
pEntity->SetKeyValue(pszKey, szNewValue);
nReplacedCount++;
}
}
}
//
// Replace connections.
//
int nConnCount = pEntity->Connections_GetCount();
for (int i = 0; i < nConnCount; i++)
{
CEntityConnection *pConn = pEntity->Connections_Get(i);
if (pConn)
{
char szNewValue[MAX_PATH];
if (ReplaceString(szNewValue, pConn->GetTargetName(), FindObject, pszReplace))
{
pConn->SetTargetName(szNewValue);
nReplacedCount++;
}
if (ReplaceString(szNewValue, pConn->GetParam(), FindObject, pszReplace))
{
pConn->SetParam(szNewValue);
nReplacedCount++;
}
}
}
return nReplacedCount;
}
BEGIN_MESSAGE_MAP(CSearchReplaceDlg, CDialog)
//{{AFX_MSG_MAP(CSearchReplaceDlg)
ON_WM_SHOWWINDOW()
ON_COMMAND_EX(IDC_FIND_NEXT, OnFindReplace)
ON_COMMAND_EX(IDC_REPLACE, OnFindReplace)
ON_COMMAND_EX(IDC_REPLACE_ALL, OnFindReplace)
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
//-----------------------------------------------------------------------------
// Purpose:
// Input : pParent -
//-----------------------------------------------------------------------------
CSearchReplaceDlg::CSearchReplaceDlg(CWnd *pParent)
: CDialog(CSearchReplaceDlg::IDD, pParent)
{
m_bNewSearch = true;
//{{AFX_DATA_INIT(CSearchReplaceDlg)
m_bVisiblesOnly = FALSE;
m_nFindIn = FindInWorld;
m_bWholeWord = FALSE;
m_bCaseSensitive = FALSE;
//}}AFX_DATA_INIT
}
//-----------------------------------------------------------------------------
// Purpose:
// Output : Returns TRUE on success, FALSE on failure.
//-----------------------------------------------------------------------------
BOOL CSearchReplaceDlg::Create(CWnd *pwndParent)
{
return CDialog::Create(CSearchReplaceDlg::IDD, pwndParent);
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : pDX -
//-----------------------------------------------------------------------------
void CSearchReplaceDlg::DoDataExchange(CDataExchange* pDX)
{
CDialog::DoDataExchange(pDX);
//{{AFX_DATA_MAP(CSearchReplaceDlg)
DDX_Check(pDX, IDC_VISIBLES_ONLY, m_bVisiblesOnly);
DDX_Check(pDX, IDC_WHOLE_WORD, m_bWholeWord);
DDX_Check(pDX, IDC_CASE_SENSITIVE, m_bCaseSensitive);
DDX_Text(pDX, IDC_FIND_TEXT, m_strFindText);
DDX_Text(pDX, IDC_REPLACE_TEXT, m_strReplaceText);
DDX_Radio(pDX, IDC_SELECTION, m_nFindIn);
//}}AFX_DATA_MAP
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CSearchReplaceDlg::OnCancel(void)
{
ShowWindow(SW_HIDE);
}
//-----------------------------------------------------------------------------
// Purpose: Fill out the find criteria from the dialog controls.
// Input : FindObject -
//-----------------------------------------------------------------------------
void CSearchReplaceDlg::GetFindCriteria(FindObject_t &FindObject, CMapDoc *pDoc)
{
FindObject.pWorld = pDoc->GetMapWorld();
if (m_nFindIn == FindInSelection)
{
FindObject.eFindIn = FindInSelection;
FindObject.SelectionList.RemoveAll();
const CMapObjectList *pSelection = pDoc->GetSelection()->GetList();
for (int i = 0; i < pSelection->Count(); i++)
{
CUtlReference< CMapClass > object = pSelection->Element(i);
if ( object->IsGroup() )
{
// If it's a group, get all the entities in the group.
const CMapObjectList *pChildren = object->GetChildren();
FOR_EACH_OBJ( *pChildren, pos )
{
FindObject.SelectionList.AddToTail( pChildren->Element(pos) );
}
}
else
{
FindObject.SelectionList.AddToTail( object );
}
}
}
else
{
FindObject.eFindIn = FindInWorld;
}
FindObject.strFindText = m_strFindText;
FindObject.bVisiblesOnly = (m_bVisiblesOnly == TRUE);
FindObject.bWholeWord = (m_bWholeWord == TRUE);
FindObject.bCaseSensitive = (m_bCaseSensitive == TRUE);
}
//-----------------------------------------------------------------------------
// Purpose: Called when they hit the Find, the Replace, or the Replace All button.
// Input : uCmd - The ID of the button the user hit, IDC_FIND_NEXT or IDC_REPLACE.
// Output : Returns TRUE to indicate that the message was handled.
//-----------------------------------------------------------------------------
BOOL CSearchReplaceDlg::OnFindReplace(UINT uCmd)
{
CMapDoc *pDoc = CMapDoc::GetActiveMapDoc();
if (!pDoc)
{
return TRUE;
}
static FindObject_t FindObject;
static CMapClass *pLastFound = NULL;
static int nReplaceCount = 0;
FindObject_t TempFindObject;
bool bDone = false;
UpdateData();
GetFindCriteria(TempFindObject, pDoc);
if ( strcmp(TempFindObject.strFindText, FindObject.strFindText) != 0 )
{
m_bNewSearch = true;
}
do
{
CMapClass *pObject = NULL;
if (m_bNewSearch)
{
//
// New search. Fetch the data from the controls.
//
UpdateData();
GetFindCriteria(FindObject, pDoc);
//
// We have to keep track of the last object in the iteration for replacement,
// because replacement is done when me advance to the next object.
//
pLastFound = NULL;
nReplaceCount = 0;
pObject = FindFirstObject(FindObject);
}
else
{
pObject = FindNextObject(FindObject);
}
//
// Replace All is undone as single operation. Mark the undo position the first time
// we find a match during a Replace All.
//
if (m_bNewSearch && (uCmd == IDC_REPLACE_ALL) && pObject)
{
GetHistory()->MarkUndoPosition(pDoc->GetSelection()->GetList(), "Replace Text");
}
//
// If we have an object to do the replace on, do the replace.
//
if (pLastFound && ((uCmd == IDC_REPLACE) || (uCmd == IDC_REPLACE_ALL)))
{
if (uCmd == IDC_REPLACE)
{
// Allow for undo each time we do a Replace.
GetHistory()->MarkUndoPosition(NULL, "Replace Text");
}
//
// Do the replace on the last matching object we found. This lets the user see what
// object will be modified before it is done.
//
GetHistory()->Keep(pLastFound);
nReplaceCount += FindReplace((CMapEntity *)pLastFound, FindObject, m_strReplaceText);
GetDlgItem(IDCANCEL)->SetWindowText("Close");
}
if (pObject)
{
//
// We found an object that satisfies our search.
//
if ((uCmd == IDC_FIND_NEXT) || (uCmd == IDC_REPLACE))
{
//
// Highlight the match.
//
pDoc->SelectObject(pObject, scClear | scSelect);
pDoc->CenterViewsOnSelection();
}
//
// Stop after one match unless we are doing a Replace All.
//
if (uCmd != IDC_REPLACE_ALL)
{
bDone = true;
}
m_bNewSearch = false;
pLastFound = pObject;
}
else
{
//
// No more objects in the search set match our criteria.
//
if ((m_bNewSearch) || (uCmd != IDC_REPLACE_ALL))
{
CString str;
str.Format("Finished searching for '%s'.", m_strFindText);
MessageBox(str, "Find/Replace Text", MB_OK);
// TODO: put the old selection back
}
else if (uCmd == IDC_REPLACE_ALL)
{
CString str;
str.Format("Replaced %d occurrences of the string '%s' with '%s'.", nReplaceCount, m_strFindText, m_strReplaceText);
MessageBox(str, "Find/Replace Text", MB_OK);
}
m_bNewSearch = true;
bDone = true;
}
} while (!bDone);
return TRUE;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CSearchReplaceDlg::OnOK()
{
}
//-----------------------------------------------------------------------------
// Purpose: Called any time we are hidden or shown.
// Input : bShow -
// nStatus -
//-----------------------------------------------------------------------------
void CSearchReplaceDlg::OnShowWindow(BOOL bShow, UINT nStatus)
{
if (bShow)
{
m_bNewSearch = true;
GetDlgItem(IDCANCEL)->SetWindowText("Cancel");
m_nFindIn = FindInWorld;
CMapDoc *pDoc = CMapDoc::GetActiveMapDoc();
if (pDoc)
{
if ( !pDoc->GetSelection()->IsEmpty() )
{
m_nFindIn = FindInSelection;
}
}
// Populate the controls with the current data.
UpdateData(FALSE);
}
CDialog::OnShowWindow(bShow, nStatus);
}

81
hammer/SearchReplaceDlg.h Normal file
View File

@@ -0,0 +1,81 @@
//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//=============================================================================//
#ifndef SEARCHREPLACEDLG_H
#define SEARCHREPLACEDLG_H
#ifdef _WIN32
#pragma once
#endif
#include "resource.h"
#include "UtlVector.h"
#include "MapClass.h"
class CMapEntity;
struct FindObject_t;
enum FindReplaceIn_t
{
FindInSelection = 0,
FindInWorld,
};
class CSearchReplaceDlg : public CDialog
{
// Construction
public:
CSearchReplaceDlg(CWnd* pParent = NULL); // standard constructor
int Create(CWnd *pwndParent = NULL);
// Dialog Data
//{{AFX_DATA(CSearchReplaceDlg)
enum { IDD = IDD_SEARCH_REPLACE };
CString m_strFindText;
CString m_strReplaceText;
BOOL m_bVisiblesOnly;
BOOL m_bWholeWord;
BOOL m_bCaseSensitive;
int m_nFindIn;
//}}AFX_DATA
// Overrides
// ClassWizard generated virtual function overrides
//{{AFX_VIRTUAL(CSearchReplaceDlg)
protected:
virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support
//}}AFX_VIRTUAL
// Implementation
protected:
// Generated message map functions
//{{AFX_MSG(CSearchReplaceDlg)
afx_msg BOOL OnFindReplace(UINT uCmd);
virtual void OnOK();
virtual void OnCancel();
virtual void OnShowWindow(BOOL bShow, UINT nStatus);
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
private:
void GetFindCriteria(FindObject_t &FindObject, CMapDoc *pDoc);
void FindFirst();
bool FindNext(bool bReplace);
bool m_bNewSearch; // Set to true every time the dialog is brought up.
};
#endif // SEARCHREPLACEDLG_H

43
hammer/SelectModeDlgBar.h Normal file
View File

@@ -0,0 +1,43 @@
//========= Copyright Š 1996-2005, Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//=============================================================================//
#ifndef SELECTMODEDLGBAR_H
#define SELECTMODEDLGBAR_H
#ifdef _WIN32
#pragma once
#endif
#include "resource.h"
#include "GroupList.h"
#include "HammerBar.h"
class CSelectModeDlgBar : public CHammerBar
{
public:
BOOL Create(CWnd *pParentWnd);
private:
//{{AFX_DATA(CFilterControl)
enum { IDD = IDD_SELECT_MODE_BAR };
//}}AFX_DATA
protected:
//{{AFX_MSG(CFilterControl)
afx_msg void OnGroups();
afx_msg void OnObjects();
afx_msg void OnSolids();
afx_msg void UpdateControlGroups(CCmdUI *pCmdUI);
afx_msg void UpdateControlObjects(CCmdUI *pCmdUI);
afx_msg void UpdateControlSolids(CCmdUI *pCmdUI);
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
};
#endif // SELECTMODEDLGBAR_H

626
hammer/Selection.cpp Normal file
View File

@@ -0,0 +1,626 @@
//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============//
//
// Purpose: The document. Exposes functions for object creation, deletion, and
// manipulation. Holds the current tool. Handles GUI messages that are
// view-independent.
//
//=============================================================================//
#include "stdafx.h"
#include "Selection.h"
#include "mapdoc.h"
#include "MapHelper.h"
#include "MapSolid.h"
#include "Manifest.h"
#include "mapdefs.h"
#include "globalfunctions.h"
#include "mainfrm.h"
#include "objectproperties.h"
CSelection::CSelection(void)
{
m_pDocument = NULL;
}
CSelection::~CSelection(void)
{
}
void CSelection::Init( CMapDoc *pDocument )
{
m_pDocument = pDocument;
m_eSelectMode = selectGroups;
m_SelectionList.RemoveAll();
ClearHitList();
m_LastValidBounds.bmins = Vector(0, 0, 0);
m_LastValidBounds.bmaxs = Vector(64, 64, 64);
UpdateSelectionBounds();
}
bool CSelection::IsSelected(CMapClass *pobj)
{
return (m_SelectionList.Find(pobj) != m_SelectionList.InvalidIndex());
}
void CSelection::GetLastValidBounds(Vector &vecMins, Vector &vecMaxs)
{
vecMins = m_LastValidBounds.bmins;
vecMaxs = m_LastValidBounds.bmaxs;
}
bool CSelection::GetBounds(Vector &vecMins, Vector &vecMaxs)
{
if ( m_bBoundsDirty )
UpdateSelectionBounds();
if ( m_SelectionList.Count() == 0)
return false;
vecMins = m_Bounds.bmins;
vecMaxs = m_Bounds.bmaxs;
return true;;
}
bool CSelection::GetLogicalBounds(Vector2D &vecMins, Vector2D &vecMaxs)
{
if ( m_bBoundsDirty )
UpdateSelectionBounds();
if ( m_SelectionList.Count() == 0)
return false;
vecMins = m_vecLogicalMins;
vecMaxs = m_vecLogicalMaxs;
return true;
}
//-----------------------------------------------------------------------------
// Purpose: Used for translations. Uses entity origins and brush bounds.
// That way, when moving stuff, the entity origins will stay on the grid.
//-----------------------------------------------------------------------------
void CSelection::GetBoundsForTranslation( Vector &vecMins, Vector &vecMaxs )
{
vecMins.Init( COORD_NOTINIT, COORD_NOTINIT, 0 );
vecMaxs.Init( -COORD_NOTINIT, -COORD_NOTINIT, 0 );
// If there are any solids, then only use the bounds for those. Otherwise,
// an entity that is off the grid can pull all the solids off the grid and you never want that.
int nSolids = 0;
for (int i = 0; i < m_SelectionList.Count(); i++)
{
CMapClass *pobj = m_SelectionList[i];
CEditGameClass *pEdit = dynamic_cast< CEditGameClass* >( pobj );
if ( (pEdit && pEdit->IsSolidClass()) || dynamic_cast<CMapSolid *>(pobj) )
{
++nSolids;
}
}
for (int i = 0; i < m_SelectionList.Count(); i++)
{
CMapClass *pobj = m_SelectionList[i];
// update physical bounds
Vector mins, maxs;
CEditGameClass *pEdit = dynamic_cast< CEditGameClass* >( pobj );
if ( (pEdit && pEdit->IsSolidClass()) || dynamic_cast<CMapSolid *>(pobj) )
{
pobj->GetRender2DBox(mins, maxs);
}
else if ( nSolids == 0 )
{
pobj->GetOrigin( mins );
maxs = mins;
}
VectorMin( mins, vecMins, vecMins );
VectorMax( maxs, vecMaxs, vecMaxs );
}
}
void CSelection::UpdateSelectionBounds( void )
{
m_Bounds.ResetBounds();
m_vecLogicalMins[0] = m_vecLogicalMins[1] = COORD_NOTINIT;
m_vecLogicalMaxs[0] = m_vecLogicalMaxs[1] = -COORD_NOTINIT;
for (int i = 0; i < m_SelectionList.Count(); i++)
{
CMapClass *pobj = m_SelectionList[i];
// update physical bounds
Vector mins,maxs;
pobj->GetRender2DBox(mins, maxs);
m_Bounds.UpdateBounds(mins, maxs);
// update logical bounds
Vector2D logicalMins,logicalMaxs;
pobj->GetRenderLogicalBox( logicalMins, logicalMaxs );
Vector2DMin( logicalMins, m_vecLogicalMins, m_vecLogicalMins );
Vector2DMax( logicalMaxs, m_vecLogicalMaxs, m_vecLogicalMaxs );
}
// remeber bounds if valid
if ( m_Bounds.IsValidBox() )
{
m_LastValidBounds = m_Bounds;
}
m_bBoundsDirty = false;
}
bool CSelection::GetBoundsCenter(Vector &vecCenter)
{
if ( m_bBoundsDirty )
UpdateSelectionBounds();
if ( m_SelectionList.Count() == 0 )
return false;
m_Bounds.GetBoundsCenter( vecCenter );
return true;
}
bool CSelection::GetLogicalBoundsCenter( Vector2D &vecCenter )
{
if ( m_bBoundsDirty )
UpdateSelectionBounds();
if ( m_SelectionList.Count() == 0 )
return false;
vecCenter = (m_vecLogicalMins+m_vecLogicalMaxs)/2;
return true;
}
bool CSelection::IsEmpty()
{
return m_SelectionList.Count() == 0;
}
const CMapObjectList *CSelection::GetList()
{
return &m_SelectionList;
}
const CMapObjectList* CSelection::GetHitList()
{
return &m_HitList;
}
int CSelection::GetCount()
{
return m_SelectionList.Count();
}
//-----------------------------------------------------------------------------
// Purpose: Returns the current selection mode. The selection mode determines
// what gets selected when the user clicks on something - the group,
// the entity, or the solid.
//-----------------------------------------------------------------------------
SelectMode_t CSelection::GetMode()
{
return m_eSelectMode;
}
void CSelection::SetSelectionState(SelectionState_t eSelectionState)
{
for ( int i=0; i<m_SelectionList.Count(); i++ )
{
CMapClass *pMapClass = (CUtlReference< CMapClass >)m_SelectionList.Element(i);
CMapEntity *pObject = (CMapEntity *)pMapClass;
pObject->SetSelectionState( eSelectionState );
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
bool CSelection::IsAnEntitySelected(void)
{
if (m_SelectionList.Count() > 0)
{
int nSelCount = m_SelectionList.Count();
for (int i = 0; i < nSelCount; i++)
{
CMapClass *pObject = m_SelectionList.Element(i);
CMapEntity *pEntity = dynamic_cast <CMapEntity *> (pObject);
if (pEntity != NULL)
{
return true;
}
}
}
return false;
}
//-----------------------------------------------------------------------------
// Purpose: Returns true if the selection is editable. Every object must be
// individually editable for this routine to return true.
//-----------------------------------------------------------------------------
bool CSelection::IsEditable()
{
if ( m_SelectionList.Count() > 0 )
{
int nSelCount = m_SelectionList.Count();
for (int i = 0; i < nSelCount; i++)
{
CMapClass *pObject = m_SelectionList.Element(i);
if ( pObject->IsEditable() == false )
{
return false;
}
}
}
return true;
}
//-----------------------------------------------------------------------------
// Purpose: Returns true if the selection is copyable. CManifestInstance classes
// are not copyable.
//-----------------------------------------------------------------------------
bool CSelection::IsCopyable()
{
if ( m_SelectionList.Count() > 0 )
{
int nSelCount = m_SelectionList.Count();
for (int i = 0; i < nSelCount; i++)
{
CMapClass *pObject = m_SelectionList.Element(i);
if ( pObject->IsMapClass( MAPCLASS_TYPE( CManifestInstance ) ) )
{
return false;
}
}
}
return true;
}
//-----------------------------------------------------------------------------
// Purpose: Sets the current selection mode, which determines which objects
// are selected when the user clicks on things.
//-----------------------------------------------------------------------------
void CSelection::SetMode(SelectMode_t eNewSelectMode)
{
SelectMode_t eOldSelectMode = m_eSelectMode;
m_eSelectMode = eNewSelectMode;
if ((eOldSelectMode == selectSolids) ||
((eOldSelectMode == selectObjects) && (eNewSelectMode == selectGroups)))
{
//
// If we are going from a more specific selection mode to a less specific one,
// clear the selection. This avoids unexpectedly selecting new things.
//
SelectObject(NULL, scClear|scSaveChanges);
}
else
{
//
// Put all the children of the selected objects in a list, along with their children.
//
CMapObjectList NewList;
int nSelCount = m_SelectionList.Count();
for (int i = 0; i < nSelCount; i++)
{
CMapClass *pObject = m_SelectionList[i];
AddLeavesToListCallback(pObject, &NewList);
pObject->EnumChildren((ENUMMAPCHILDRENPROC)AddLeavesToListCallback, (DWORD)&NewList);
}
SelectObject(NULL, scClear|scSaveChanges);
//
// Add child objects to selection.
//
for (int pos=0;pos<NewList.Count();pos++)
{
CMapClass *pObject = NewList[pos];
CMapClass *pSelObject = pObject->PrepareSelection(eNewSelectMode);
if (pSelObject)
{
SelectObject(pSelObject, scSelect);
}
}
}
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : *pObject -
//-----------------------------------------------------------------------------
void CSelection::AddHit(CMapClass *pObject)
{
if ( m_HitList.Find(pObject) == -1 )
{
m_HitList.AddToTail(pObject);
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CSelection::ClearHitList(void)
{
m_HitList.RemoveAll();
m_iCurHit = -1;
}
bool CSelection::RemoveAll(void)
{
for ( int i=0;i<m_SelectionList.Count(); i++ )
{
CMapClass *pObject = m_SelectionList.Element(i);
pObject->SetSelectionState(SELECT_NONE);
}
m_SelectionList.RemoveAll();
SetBoundsDirty();
return true;
}
bool CSelection::RemoveDead(void)
{
bool bFoundOne = false;
for ( int i=m_SelectionList.Count()-1; i>=0; i-- )
{
CMapClass *pObject = m_SelectionList.Element(i);
if (!pObject->GetParent())
{
m_SelectionList.FastRemove(i);
pObject->SetSelectionState(SELECT_NONE);
bFoundOne = true;
}
}
// TODO check if we do the same as in SelectObject
SetBoundsDirty();
return bFoundOne;
}
//-----------------------------------------------------------------------------
// Purpose: Removes objects that are not visible from the selection set.
//-----------------------------------------------------------------------------
bool CSelection::RemoveInvisibles(void)
{
bool bFoundOne = false;
for ( int i=m_SelectionList.Count()-1; i>=0; i-- )
{
CMapClass *pObject = m_SelectionList.Element(i);
if ( !pObject->IsVisible() )
{
m_SelectionList.FastRemove(i);
pObject->SetSelectionState(SELECT_NONE);
bFoundOne = true;
}
}
SetBoundsDirty();
return bFoundOne;
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : iIndex -
// bUpdateViews -
//-----------------------------------------------------------------------------
void CSelection::SetCurrentHit(int iIndex, bool bCascading)
{
if ( m_HitList.Count() == 0)
{
Assert( m_iCurHit == -1);
return;
}
// save & toggle old selection off
if (m_iCurHit != -1)
{
CMapClass *pObject = m_HitList[m_iCurHit];
SelectObject(pObject, scToggle|scSaveChanges);
}
if (iIndex == hitNext)
{
// hit next object
m_iCurHit++;
}
else if (iIndex == hitPrev)
{
// hit prev object
m_iCurHit--;
}
else
{
m_iCurHit = iIndex;
}
// make sure curhit is valid
if (m_iCurHit >= m_HitList.Count())
{
m_iCurHit = 0;
}
else if (m_iCurHit < 0)
{
m_iCurHit = m_HitList.Count() - 1;
}
CMapClass *pObject = m_HitList[m_iCurHit];
if ( bCascading )
{
// Build actual selection list based on cascading...
CUtlRBTree< CMapClass*, unsigned short > tree( 0, 0, DefLessFunc( CMapClass* ) );
bool bRecursive = false; // not used yet
m_pDocument->BuildCascadingSelectionList( pObject, tree, bRecursive );
CMapObjectList list;
list.AddToTail( pObject );
bool bRootIsSelected = IsSelected(pObject);
bool bUniformSelectionState = true;
for ( unsigned short h = tree.FirstInorder(); h != tree.InvalidIndex(); h = tree.NextInorder(h) )
{
list.AddToTail( list[h] );
if ( IsSelected( list[h] ) != bRootIsSelected )
{
bUniformSelectionState = false;
}
}
/* Change toggle to select or unselect if we're toggling and cascading
// but the root + children have different selection state
if ( ( !bUniformSelectionState ) && ( cmd == scToggle ) )
{
cmd = bRootIsSelected ? scSelect : scUnselect;
}*/
SelectObjectList( &list, scSelect );
}
else
{
SelectObject(pObject, scToggle );
}
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : pobj -
// cmd -
//-----------------------------------------------------------------------------
bool CSelection::SelectObject(CMapClass *pObj, int cmd)
{
// if no object is given we only can execute the clear command
if ( pObj == NULL )
{
// check if selection is already empty
if (m_SelectionList.Count() == 0)
return false; // nothing to do
if ( cmd & scClear )
{
RemoveAll();
}
}
else // object oriented operation
{
int iIndex = m_SelectionList.Find(pObj);
bool bAlreadySelected = iIndex != -1;
if ( cmd & scToggle )
{
if ( bAlreadySelected )
cmd |= scUnselect;
else
cmd |= scSelect;
}
if ( cmd & scSelect )
{
if ( cmd & scClear )
{
// if we re-selected the only selected element, nothing changes
if ( bAlreadySelected && m_SelectionList.Count() == 1 )
return false;
RemoveAll();
bAlreadySelected = false; // reset that flag
}
if ( bAlreadySelected )
return false;
m_SelectionList.AddToTail(pObj);
pObj->SetSelectionState(SELECT_NORMAL);
}
else if ( (cmd & scUnselect) && bAlreadySelected )
{
// ok unselect an yet selected object
m_SelectionList.FastRemove(iIndex);
pObj->SetSelectionState(SELECT_NONE);
}
else
{
return false; // nothing was changed
}
}
// ok something in the selection was changed, set dirty flags
SetBoundsDirty();
if ( cmd & scSaveChanges )
{
// changing the selection automatically saves changes made to the properties dialog
GetMainWnd()->pObjectProperties->SaveData( SAVEDATA_SELECTION_CHANGED );
}
// always mark data dirty
GetMainWnd()->pObjectProperties->MarkDataDirty();
// uddate all views
m_pDocument->UpdateAllViews( MAPVIEW_UPDATE_SELECTION );
return true;
}
//-----------------------------------------------------------------------------
// Purpose: Clears the current selection and selects everything in the given list.
// Input : pList - Objects to select.
//-----------------------------------------------------------------------------
void CSelection::SelectObjectList( const CMapObjectList *pList, int cmd )
{
// Clear the current selection.
// Clear the current selection.
if ( cmd & scSaveChanges )
{
GetMainWnd()->pObjectProperties->SaveData( SAVEDATA_SELECTION_CHANGED );
cmd &= ~scSaveChanges;
}
if ( cmd & scClear )
{
RemoveAll();
cmd &= ~scClear;
}
if ( pList != NULL )
{
for (int pos=0;pos<pList->Count();pos++)
{
CMapClass *pObject = (CUtlReference< CMapClass >)pList->Element(pos);
CMapClass *pSelObject = pObject->PrepareSelection( m_eSelectMode );
if (pSelObject)
{
SelectObject( pSelObject, cmd );
}
}
}
}

116
hammer/Selection.h Normal file
View File

@@ -0,0 +1,116 @@
//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============//
//
// Purpose: The document. Exposes functions for object creation, deletion, and
// manipulation. Holds the current tool. Handles GUI messages that are
// view-independent.
//
//=============================================================================//
#ifndef SELECTIONMANAGER_H
#define SELECTIONMANAGER_H
#ifdef _WIN32
#pragma once
#endif
#include "mapclass.h"
class CMapDoc;
enum
{
hitFirst = 0,
hitNext = -1,
hitPrev = -2
};
// SelectObject/SelectFace parameters:
typedef enum
{
scToggle = 0x01, // toogle selection state of this object
scSelect = 0x02, // select this object
scUnselect = 0x04, // unselect this object
scClear = 0x10, // Clear current before selecting
scNoLift = 0x20, // Don't lift face attributes into Face Properties dialog dvs: lame!
scNoApply = 0x40, // Don't apply face attributes from Face Properties dialog to selected face dvs: lame!
scCascading = 0x80, // Select all entities attached to outputs of this entity
scCascadingRecursive = 0x100, // Select all entities attached to outputs of this entity, recursively
scSelectAll = 0x200,
scSaveChanges = 0x400, // changing the selection causes changes made in the properties dialog be saved
};
class CSelection
{
public:
CSelection(void);
virtual ~CSelection(void);
void Init(CMapDoc *pDocument);
bool SelectObject(CMapClass *pobj, int cmd = scSelect);
void SelectObjectList(const CMapObjectList *pList, int cmd = (scClear|scSelect|scSaveChanges) );
bool RemoveAll(); // true if any elements were removed
bool RemoveInvisibles(); // true if any elements were removed
bool RemoveDead(); // true if any elements were removed
int GetCount();
bool IsEmpty();
bool IsSelected(CMapClass *pObject);
bool IsAnEntitySelected();
bool IsEditable();
bool IsCopyable();
const CMapObjectList* GetList(void);
CMapDoc *GetMapDoc() { return m_pDocument; }
// HitList feature
const CMapObjectList* GetHitList(void);
void ClearHitList();
void AddHit(CMapClass *pObject);
void SetCurrentHit(int iIndex, bool bCascading = false);
SelectMode_t GetMode(void);
void SetMode(SelectMode_t eSelectMode);
void SetSelectionState(SelectionState_t eSelectionState);
bool GetBounds(Vector &vecMins, Vector &vecMaxs);
// Used for translations. Uses entity origins and brush bounds. That way, when moving stuff,
// the entity origins will stay on the grid.
void GetBoundsForTranslation( Vector &vecMins, Vector &vecMaxs );
bool GetBoundsCenter(Vector &vecCenter);
void GetLastValidBounds(Vector &vecMins, Vector &vecMaxs);
bool GetLogicalBounds(Vector2D &vecMins, Vector2D &vecMaxs);
bool GetLogicalBoundsCenter( Vector2D &vecCenter );
void SetBoundsDirty() {m_bBoundsDirty = true;}
protected:
void UpdateSelectionBounds();
CMapDoc *m_pDocument; // document this selection set belongs to
SelectMode_t m_eSelectMode; // Controls what gets selected based on what the user clicked on.
CMapObjectList m_SelectionList; // The list of selected objects.
bool m_bBoundsDirty; // recalc bounds box with next query
BoundBox m_Bounds; // current bounds
BoundBox m_LastValidBounds; // last valid selection bounds
Vector2D m_vecLogicalMins; // Selection bounds in "logical" space
Vector2D m_vecLogicalMaxs;
// Hit selection.
CMapObjectList m_HitList; // list of 'hit' object (potential selected object)
int m_iCurHit; // current hit or -1
};
#endif // SELECTIONMANAGER_H

View File

@@ -0,0 +1,416 @@
//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
//=============================================================================//
#include <stdafx.h>
#include "smoothinggroupmgr.h"
#include "mapface.h"
#include "ChunkFile.h"
class CSmoothingGroupMgr : public ISmoothingGroupMgr
{
public:
CSmoothingGroupMgr();
~CSmoothingGroupMgr();
SmoothingGroupHandle_t CreateGroup( void );
void DestroyGroup( SmoothingGroupHandle_t hGroup );
bool IsGroup( SmoothingGroupHandle_t hGroup );
void AddFaceToGroup( SmoothingGroupHandle_t hGroup, CMapFace *pFace );
void RemoveFaceFromGroup( SmoothingGroupHandle_t hGroup, CMapFace *pFace );
void SetGroupSmoothingAngle( SmoothingGroupHandle_t hGroup, float flAngle );
float GetGroupSmoothingAngle( SmoothingGroupHandle_t hGroup );
int GetFaceCountInGroup( SmoothingGroupHandle_t hGroup );
CMapFace *GetFaceFromGroup( SmoothingGroupHandle_t hGroup, int iFace );
ChunkFileResult_t SaveVMF( CChunkFile *pFile, CSaveInfo *pSaveInfo );
ChunkFileResult_t LoadVMF( CChunkFile *pFile );
private:
#if 0
static ChunkFileResult_t LoadSmoothingGroupMgrCallback( const char *szKey, const char *szValue, CSmoothingGroupMgr *pSmoothingGroupMgr );
static ChunkFileResult_t LoadSmoothingGroupMgrKeyCallback( const char *szKey, const char *szValue, CSmoothingGroupMgr *pSmoothingGroupMgr );
static ChunkFileResult_t LoadSmoothingGroupCallback( CChunkFile *pFile, SmoothingGroup_t *pGroup );
static ChunkFileResult_t LoadSmoothingGroupKeyCallback( const char *szKey, const char *szValue, SmoothingGroup_t *pGroup );
#endif
private:
struct SmoothingGroup_t
{
int m_nID;
CUtlVector<CMapFace*> m_aFaces;
float m_flSmoothingAngle;
};
CUtlVector<SmoothingGroup_t> m_aSmoothingGroups;
};
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
ISmoothingGroupMgr *SmoothingGroupMgr( void )
{
static CSmoothingGroupMgr s_SmoothingGroupMgr;
return &s_SmoothingGroupMgr;
}
//-----------------------------------------------------------------------------
// Purpose: Constructor
//-----------------------------------------------------------------------------
CSmoothingGroupMgr::CSmoothingGroupMgr()
{
m_aSmoothingGroups.SetSize( MAX_SMOOTHING_GROUP_COUNT );
}
//-----------------------------------------------------------------------------
// Purpose: Deconstructor
//-----------------------------------------------------------------------------
CSmoothingGroupMgr::~CSmoothingGroupMgr()
{
m_aSmoothingGroups.Purge();
}
//-----------------------------------------------------------------------------
// Purpose: Add a face to the smoothing group.
//-----------------------------------------------------------------------------
void CSmoothingGroupMgr::AddFaceToGroup( SmoothingGroupHandle_t hGroup, CMapFace *pFace )
{
// Validate data.
Assert( hGroup != INVALID_SMOOTHING_GROUP );
Assert( hGroup >= 0 );
Assert( hGroup < MAX_SMOOTHING_GROUP_COUNT );
int iGroup = static_cast<int>( hGroup );
SmoothingGroup_t *pGroup = &m_aSmoothingGroups[iGroup];
if ( pGroup )
{
// Check to see if we already have this face in this group.
if ( pGroup->m_aFaces.Find( pFace ) == -1 )
{
pFace->AddSmoothingGroupHandle( hGroup );
pGroup->m_aFaces.AddToTail( pFace );
}
}
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CSmoothingGroupMgr::RemoveFaceFromGroup( SmoothingGroupHandle_t hGroup, CMapFace *pFace )
{
// Validate data.
Assert( hGroup != INVALID_SMOOTHING_GROUP );
Assert( hGroup >= 0 );
Assert( hGroup < MAX_SMOOTHING_GROUP_COUNT );
int iGroup = static_cast<int>( hGroup );
SmoothingGroup_t *pGroup = &m_aSmoothingGroups[iGroup];
if ( pGroup )
{
int iFace = pGroup->m_aFaces.Find( pFace );
if ( iFace != -1 )
{
pFace->RemoveSmoothingGroupHandle( hGroup );
pGroup->m_aFaces.Remove( iFace );
}
}
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CSmoothingGroupMgr::SetGroupSmoothingAngle( SmoothingGroupHandle_t hGroup, float flAngle )
{
// Validate data.
Assert( hGroup != INVALID_SMOOTHING_GROUP );
Assert( hGroup >= 0 );
Assert( hGroup < MAX_SMOOTHING_GROUP_COUNT );
int iGroup = static_cast<int>( hGroup );
SmoothingGroup_t *pGroup = &m_aSmoothingGroups[iGroup];
if ( pGroup )
{
pGroup->m_flSmoothingAngle = flAngle;
}
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
float CSmoothingGroupMgr::GetGroupSmoothingAngle( SmoothingGroupHandle_t hGroup )
{
// Validate data.
Assert( hGroup != INVALID_SMOOTHING_GROUP );
Assert( hGroup >= 0 );
Assert( hGroup < MAX_SMOOTHING_GROUP_COUNT );
int iGroup = static_cast<int>( hGroup );
SmoothingGroup_t *pGroup = &m_aSmoothingGroups[iGroup];
if ( pGroup )
{
return pGroup->m_flSmoothingAngle;
}
return -1.0f;
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
int CSmoothingGroupMgr::GetFaceCountInGroup( SmoothingGroupHandle_t hGroup )
{
// Validate data.
Assert( hGroup != INVALID_SMOOTHING_GROUP );
Assert( hGroup >= 0 );
Assert( hGroup < MAX_SMOOTHING_GROUP_COUNT );
int iGroup = static_cast<int>( hGroup );
SmoothingGroup_t *pGroup = &m_aSmoothingGroups[iGroup];
if ( pGroup )
{
return pGroup->m_aFaces.Count();
}
return -1;
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
CMapFace *CSmoothingGroupMgr::GetFaceFromGroup( SmoothingGroupHandle_t hGroup, int iFace )
{
// Validate data.
Assert( hGroup != INVALID_SMOOTHING_GROUP );
Assert( hGroup >= 0 );
Assert( hGroup < MAX_SMOOTHING_GROUP_COUNT );
int iGroup = static_cast<int>( hGroup );
SmoothingGroup_t *pGroup = &m_aSmoothingGroups[iGroup];
if ( pGroup )
{
return pGroup->m_aFaces[iFace];
}
return NULL;
}
//-----------------------------------------------------------------------------
// Purpose: Save the smoothing group data.
//-----------------------------------------------------------------------------
ChunkFileResult_t CSmoothingGroupMgr::SaveVMF( CChunkFile *pFile, CSaveInfo *pSaveInfo )
{
int nGroupCount = 0;
for ( int iGroup = 0; iGroup < MAX_SMOOTHING_GROUP_COUNT; ++iGroup )
{
if ( m_aSmoothingGroups[iGroup].m_aFaces.Count() != 0 )
{
nGroupCount++;
}
}
if ( nGroupCount == 0 )
return ChunkFile_Ok;
ChunkFileResult_t eResult = pFile->BeginChunk( "smoothing_groups" );
for ( iGroup = 0; iGroup < MAX_SMOOTHING_GROUP_COUNT; ++iGroup )
{
SmoothingGroup_t *pGroup = &m_aSmoothingGroups[iGroup];
int nFaceCount = pGroup->m_aFaces.Count();
if ( nFaceCount == 0 )
continue;
char szBuf[MAX_KEYVALUE_LEN];
char szTemp[80];
// Save the smoothing group.
if ( eResult == ChunkFile_Ok )
{
eResult = pFile->BeginChunk( "group" );
if ( eResult == ChunkFile_Ok )
{
eResult = pFile->WriteKeyValueInt( "id", iGroup );
if ( eResult == ChunkFile_Ok )
{
eResult = pFile->WriteKeyValueFloat( "angle", pGroup->m_flSmoothingAngle );
}
if ( eResult == ChunkFile_Ok )
{
eResult = pFile->WriteKeyValueInt( "number_faces", nFaceCount );
}
if ( eResult == ChunkFile_Ok )
{
int nColCount = 20;
int nRowCount = ( nFaceCount / nColCount ) + 1;
for ( int iRow = 0; iRow < nRowCount; ++iRow )
{
bool bFirst = true;
szBuf[0] = '\0';
for ( int iCol = 0; iCol < nColCount; ++iCol )
{
int iFace = ( iRow * 20 ) + iCol;
if ( iFace >= nFaceCount )
continue;
if (!bFirst)
{
strcat(szBuf, " ");
}
CMapFace *pFace = pGroup->m_aFaces[iFace];
if ( pFace )
{
bFirst = false;
sprintf( szTemp, "%d", pFace->GetFaceID() );
strcat( szBuf, szTemp );
}
}
char szKey[10];
sprintf( szKey, "row%d", iRow );
eResult = pFile->WriteKeyValue( szKey, szBuf );
}
}
}
if ( eResult == ChunkFile_Ok )
{
eResult = pFile->EndChunk();
}
}
}
if ( eResult == ChunkFile_Ok )
{
eResult = pFile->EndChunk();
}
return eResult;
}
//-----------------------------------------------------------------------------
// Purpose: Load smoothing group data.
//-----------------------------------------------------------------------------
ChunkFileResult_t CSmoothingGroupMgr::LoadVMF( CChunkFile *pFile )
{
// ChunkFileResult_t eResult = pFile->ReadChunk( ( KeyHandler_t )LoadSmoothingGroupMgrCallback, this );
// return eResult;
return ChunkFile_Ok;
}
#if 0
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
ChunkFileResult_t CSmoothingGroupMgr::LoadSmoothingGroupMgrCallback( const char *szKey, const char *szValue,
CSmoothingGroupMgr *pSmoothingGroupMgr )
{
// Get a pointer to the next available smoothing group slot.
SmoothingGroup_t *pGroup = new SmoothingGroup_t;
if ( !pGroup )
return;
// Set up handlers for the subchunks that we are interested in.
CChunkHandlerMap Handlers;
Handlers.AddHandler( "group", ( ChunkHandler_t )LoadsSmoothingGroupCallback, SmoothingGroup_t *pGroup );
pFile->PushHandlers( &Handlers );
ChunkFileResult_t eResult = pFile->ReadChunk( ( KeyHandler_t )LoadSmoothingGroupMgrCallback, this );
pFile->PopHandlers();
if ( eResult == ChunkFile_Ok )
{
pGroup->m_nID
SmoothingGroup_t *pLoadGroup = &pSmoothingGroupMgr->m_aSmoothingGroups[pGroup->m_nID];
if ( pLoadGroup )
{
pLoadGroup->m_nID = pGroup->m_nID;
pLoadGroup->m_flSmoothingAngle = pGroup->m_flSmoothingAngle;
pLoadGroup->m_aFaces.CopyArray( pGroup->m_aFaces.Base(), pGroup->m_aFaces.Count() );
}
}
return eResult;
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
ChunkFileResult_t CSmoothingGroupMgr::LoadSmoothingGroupMgrKeyCallback( const char *szKey, const char *szValue,
CSmoothingGroupMgr *pSmoothingGroupMgr )
{
return;
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
ChunkFileResult_t CSmoothingGroupMgr::LoadSmoothingGroupCallback( CChunkFile *pFile, SmoothingGroup_t *pGroup )
{
return( pFile->ReadChunk( ( KeyHandler_t )LoadDispNormalsKeyCallback, pGroup ) );
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
ChunkFileResult_t CSmoothingGroupMgr::LoadSmoothingGroupKeyCallback( const char *szKey, const char *szValue, SmoothingGroup_t *pGroup )
{
int nId;
if ( !strnicmp( szKey, "id", 2 ) )
{
CChunkFile::ReadKeyValueInt( szValue, pGroup->m_nID );
}
if ( !strnicmp( szKey, "angle", 5 ) )
{
CChunkFile::ReadKeyValueFloat( szValue, pGroup->m_flSmoothingAngle );
}
if ( !strnicmp( szKey, "number_faces", 12 ) )
{
int nFaceCount;
CChunkFile::ReadKeyValueInt( szValue, nFaceCount );
pGroup->m_aFaces.SetSize( nFaceCount );
}
if ( !strnicmp(szKey, "row", 3 ) )
{
CMapDoc *pDoc = CMapDoc::GetActiveMapDoc();
char szBuf[MAX_KEYVALUE_LEN];
strcpy( szBuf, szValue );
int iRow = atoi( &szKey[3] );
char *pszNext = strtok( szBuf, " " );
int nIndex = nRow * 20;
int nFaceID;
while ( pszNext != NULL )
{
nFaceID = ( float )atof( pszNext );
CMapFace *pFace =
CMapFace *CMapWorld::FaceID_FaceForID(int nFaceID)
pszNext = strtok( NULL, " " );
nIndex++;
}
}
return ChunkFile_Ok ;
}
#endif

View File

@@ -0,0 +1,45 @@
//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
//=============================================================================//
#ifndef SMOOTHINGGROUPMGR_H
#define SMOOTHINGGROUPMGR_H
#pragma once
class CMapFace;
class CChunkFile;
class CSaveInfo;
enum ChunkFileResult_t;
#define MAX_SMOOTHING_GROUP_COUNT 32
#define INVALID_SMOOTHING_GROUP 0xff
typedef unsigned char SmoothingGroupHandle_t;
//=============================================================================
//
// Smoothing Group Manager
//
class ISmoothingGroupMgr
{
public:
virtual void AddFaceToGroup( SmoothingGroupHandle_t hGroup, CMapFace *pFace ) = 0;
virtual void RemoveFaceFromGroup( SmoothingGroupHandle_t hGroup, CMapFace *pFace ) = 0;
virtual void SetGroupSmoothingAngle( SmoothingGroupHandle_t hGroup, float flAngle ) = 0;
virtual float GetGroupSmoothingAngle( SmoothingGroupHandle_t hGroup ) = 0;
virtual int GetFaceCountInGroup( SmoothingGroupHandle_t hGroup ) = 0;
virtual CMapFace *GetFaceFromGroup( SmoothingGroupHandle_t hGroup, int iFace ) = 0;
virtual ChunkFileResult_t SaveVMF( CChunkFile *pFile, CSaveInfo *pSaveInfo ) = 0;
virtual ChunkFileResult_t LoadVMF( CChunkFile *pFile ) = 0;
};
ISmoothingGroupMgr *SmoothingGroupMgr( void );
#endif // SMOOTHINGGROUPMGR_H

378
hammer/SoundBrowser.cpp Normal file
View File

@@ -0,0 +1,378 @@
//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
//=============================================================================//
#include "stdafx.h"
#include "hammer.h"
#include "SoundBrowser.h"
#include "mmsystem.h"
// memdbgon must be the last include file in a .cpp file!!!
#include <tier0/memdbgon.h>
static LPCTSTR s_pszSection = "SoundBrowser";
CStringArray CSoundBrowser::m_FilterHistory;
int CSoundBrowser::m_nFilterHistory;
/////////////////////////////////////////////////////////////////////////////
// CSoundBrowser dialog
CSoundBrowser::CSoundBrowser( const char *pCurrentSoundName, CWnd* pParent /*=NULL*/ )
: CDialog(CSoundBrowser::IDD, pParent)
{
//{{AFX_DATA_INIT(CSoundBrowser)
m_Autoplay = FALSE;
m_SoundFile = _T("");
m_SoundSource = _T("");
//}}AFX_DATA_INIT
m_SoundNameSelected = pCurrentSoundName;
m_SoundType = AfxGetApp()->GetProfileInt(s_pszSection, "Sound Type", 0);
m_Autoplay = AfxGetApp()->GetProfileInt(s_pszSection, "Sound Autoplay", 0);
Q_strncpy(m_szFilter, (LPCSTR)(AfxGetApp()->GetProfileString(s_pszSection, "Sound Filter", "")), 256 );
m_nSelectedSoundIndex = -1;
// m_bSoundPlayed = false;
}
void CSoundBrowser::SaveValues()
{
AfxGetApp()->WriteProfileInt(s_pszSection, "Sound Type", m_SoundType);
AfxGetApp()->WriteProfileInt(s_pszSection, "Sound Autoplay", m_Autoplay);
AfxGetApp()->WriteProfileString(s_pszSection, "Sound Filter", m_szFilter);
}
void CSoundBrowser::DoDataExchange(CDataExchange* pDX)
{
CDialog::DoDataExchange(pDX);
//{{AFX_DATA_MAP(CSoundBrowser)
DDX_Control(pDX, IDC_SOUND_LIST, m_SoundList);
DDX_Text(pDX, IDC_SOUNDNAME_SELECTED, m_SoundNameSelected);
DDX_CBIndex(pDX, IDC_SOUND_TYPE, m_SoundType);
DDX_Check(pDX, IDC_AUTOPLAY, m_Autoplay);
DDX_Text(pDX, IDC_SOUND_FILE, m_SoundFile);
DDX_Text(pDX, IDC_SOUND_SOURCE_FILE, m_SoundSource);
//}}AFX_DATA_MAP
}
BEGIN_MESSAGE_MAP(CSoundBrowser, CDialog)
//{{AFX_MSG_MAP(CSoundBrowser)
ON_WM_CLOSE()
ON_CBN_EDITCHANGE(IDC_SOUND_FILTER, OnChangeFilter)
ON_CBN_SELENDOK(IDC_SOUND_FILTER, OnUpdateFilterNOW)
ON_CBN_SELCHANGE(IDC_SOUND_TYPE, OnSelchangeSoundType)
ON_LBN_SELCHANGE(IDC_SOUND_LIST, OnSelchangeSoundList)
ON_LBN_DBLCLK(IDC_SOUND_LIST, OnDblclkSoundList)
ON_BN_CLICKED(IDC_PREVIEW, OnPreview)
ON_BN_CLICKED(IDC_AUTOPLAY, OnAutoplay)
ON_BN_CLICKED(IDC_STOPSOUND, OnBnClickedStopsound)
ON_BN_CLICKED(IDC_REFRESH_SOUNDS, OnRefreshSounds)
ON_WM_TIMER()
ON_BN_CLICKED(IDC_OPEN_SOURCE, OnOpenSource)
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
/////////////////////////////////////////////////////////////////////////////
// CSoundBrowser message handlers
BOOL CSoundBrowser::OnInitDialog()
{
CDialog::OnInitDialog();
m_cFilter.SubclassDlgItem(IDC_SOUND_FILTER, this);
for ( int i = 0; i < m_nFilterHistory; ++i )
{
m_cFilter.AddString( m_FilterHistory[i] );
}
m_cFilter.SetWindowText(m_szFilter);
CString temp = m_szFilter;
OnFilterChanged( temp );
// Select an entry in the list that has the same name as the one passed in
int nIndex = m_SoundList.FindString( -1, m_SoundNameSelected );
if ( nIndex != LB_ERR)
{
m_SoundList.SetCurSel( nIndex );
m_nSelectedSoundIndex = nIndex;
int nSoundIndex = m_SoundList.GetItemData(nIndex);
m_SoundFile = g_Sounds.SoundFile( GetSoundType(), nSoundIndex );
m_SoundSource = g_Sounds.SoundSourceFile( GetSoundType(), nSoundIndex );
UpdateData( FALSE );
}
SetTimer(1, 500, NULL);
return TRUE;
}
void CSoundBrowser::OnClose(void)
{
Shutdown();
CDialog::OnClose();
}
//-----------------------------------------------------------------------------
// Shutdown
//-----------------------------------------------------------------------------
void CSoundBrowser::Shutdown()
{
SaveValues();
PlaySound( NULL, NULL, SND_FILENAME | SND_NODEFAULT);
// save current filter string
int i;
for (i = 0; i < m_nFilterHistory; i++)
{
if (!m_FilterHistory[i].CompareNoCase(m_szFilter))
break;
}
if(i != m_nFilterHistory) // delete first
{
m_FilterHistory.RemoveAt(i);
--m_nFilterHistory;
}
if ( m_szFilter[0] )
{
m_FilterHistory.InsertAt(0, m_szFilter);
++m_nFilterHistory;
}
}
//-----------------------------------------------------------------------------
// Clears, fills sound list
//-----------------------------------------------------------------------------
void CSoundBrowser::ClearSoundList()
{
m_SoundList.ResetContent();
}
//-----------------------------------------------------------------------------
// Sound filter
//-----------------------------------------------------------------------------
bool CSoundBrowser::ShowSoundInList( const char *pSoundName )
{
for (int i = 0; i < m_nFilters; i++)
{
if ( Q_stristr(pSoundName, m_Filters[i]) == NULL )
return false;
}
return true;
}
void CSoundBrowser::PopulateSoundList()
{
m_SoundList.SetRedraw( FALSE );
ClearSoundList();
SoundType_t type = GetSoundType();
for ( int i = g_Sounds.SoundCount( type ); --i >= 0; )
{
const char *pSoundName = g_Sounds.SoundName( type, i );
if ( ShowSoundInList( pSoundName ) )
{
CString str;
str.Format( _T(pSoundName) );
int nIndex = m_SoundList.AddString( str );
m_SoundList.SetItemDataPtr( nIndex, (PVOID)i );
}
}
m_SoundList.SetRedraw( TRUE );
}
//-----------------------------------------------------------------------------
// Sound type
//-----------------------------------------------------------------------------
SoundType_t CSoundBrowser::GetSoundType() const
{
if ( m_SoundType == 0 )
return SOUND_TYPE_GAMESOUND;
else if ( m_SoundType == 1 )
return SOUND_TYPE_RAW;
else
return SOUND_TYPE_SCENE;
}
//-----------------------------------------------------------------------------
// Sound name
//-----------------------------------------------------------------------------
void CSoundBrowser::CopySoundNameToSelected()
{
UpdateData( TRUE );
int nIndex = m_SoundList.GetCurSel();
if ( nIndex != LB_ERR )
{
int nSoundIndex = m_SoundList.GetItemData(nIndex);
m_SoundNameSelected = g_Sounds.SoundName( GetSoundType(), nSoundIndex );
m_SoundFile = g_Sounds.SoundFile( GetSoundType(), nSoundIndex );
m_SoundSource = g_Sounds.SoundSourceFile( GetSoundType(), nSoundIndex );
m_nSelectedSoundIndex = nSoundIndex;
UpdateData( FALSE );
}
}
//-----------------------------------------------------------------------------
// Update the filter:
//-----------------------------------------------------------------------------
void CSoundBrowser::OnFilterChanged( const char *pFilter )
{
Q_strncpy( m_szFilter, pFilter, 256 );
m_nFilters = 0;
char *p = strtok(m_szFilter, " ,;");
while (p != NULL)
{
m_Filters[m_nFilters++] = p;
p = strtok(NULL, " ,;");
}
PopulateSoundList();
}
//-----------------------------------------------------------------------------
// Purpose: Timer used to control updates when the filter terms change.
// Input : nIDEvent -
//-----------------------------------------------------------------------------
void CSoundBrowser::OnTimer(UINT nIDEvent)
{
if (!m_bFilterChanged)
return;
if ((time(NULL) - m_uLastFilterChange) > 0)
{
KillTimer(nIDEvent);
m_bFilterChanged = FALSE;
CString str;
m_cFilter.GetWindowText(str);
OnFilterChanged( str );
SetTimer(nIDEvent, 500, NULL);
}
CDialog::OnTimer(nIDEvent);
}
//-----------------------------------------------------------------------------
// Purpose: Called when either the filter combo or the keywords combo text changes.
//-----------------------------------------------------------------------------
void CSoundBrowser::OnChangeFilter()
{
// Start a timer to repaint the texture window using the new filters.
m_uLastFilterChange = time(NULL);
m_bFilterChanged = true;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CSoundBrowser::OnUpdateFilterNOW()
{
m_uLastFilterChange = time(NULL);
m_bFilterChanged = FALSE;
CString str;
int iSel = m_cFilter.GetCurSel();
m_cFilter.GetLBText(iSel, str);
OnFilterChanged( str );
}
//-----------------------------------------------------------------------------
// Sound type changed
//-----------------------------------------------------------------------------
void CSoundBrowser::OnSelchangeSoundType()
{
UpdateData( TRUE );
PopulateSoundList();
}
//-----------------------------------------------------------------------------
// Selected sound
//-----------------------------------------------------------------------------
const char *CSoundBrowser::GetSelectedSound()
{
return m_SoundNameSelected;
}
void CSoundBrowser::OnSelchangeSoundList()
{
CopySoundNameToSelected();
if ( m_Autoplay )
{
OnPreview();
}
}
void CSoundBrowser::OnDblclkSoundList()
{
CopySoundNameToSelected();
OnOK();
}
void CSoundBrowser::OnPreview()
{
if ( m_nSelectedSoundIndex >= 0 )
{
g_Sounds.Play( GetSoundType(), m_nSelectedSoundIndex );
}
}
void CSoundBrowser::OnAutoplay()
{
UpdateData( TRUE );
}
void CSoundBrowser::OnRefreshSounds()
{
// Set the title to "refreshing sounds..."
CString oldTitle, newTitle;
newTitle.LoadString( IDS_REFRESHING_SOUNDS );
GetWindowText( oldTitle );
SetWindowText( newTitle );
g_Sounds.Initialize();
PopulateSoundList();
// Restore the title.
SetWindowText( oldTitle );
}
int CSoundBrowser::DoModal()
{
int nRet = CDialog::DoModal();
Shutdown();
return nRet;
}
void CSoundBrowser::OnOpenSource()
{
if ( m_nSelectedSoundIndex >= 0 )
{
g_Sounds.OpenSource( GetSoundType(), m_nSelectedSoundIndex );
}
}
void CSoundBrowser::OnBnClickedStopsound()
{
g_Sounds.StopSound();
}

101
hammer/SoundBrowser.h Normal file
View File

@@ -0,0 +1,101 @@
//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
//=============================================================================//
#if !defined(AFX_SOUNDBROWSER_H__33046A12_7CF9_4031_AD10_A76200E73280__INCLUDED_)
#define AFX_SOUNDBROWSER_H__33046A12_7CF9_4031_AD10_A76200E73280__INCLUDED_
#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000
// SoundBrowser.h : header file
//
#include "soundsystem.h"
#include "AutoSelCombo.h"
/////////////////////////////////////////////////////////////////////////////
// CSoundBrowser dialog
class CSoundBrowser : public CDialog
{
// Construction
public:
CSoundBrowser( const char *pCurrentSoundName, CWnd* pParent = NULL); // standard constructor
const char *GetSelectedSound();
// Dialog Data
//{{AFX_DATA(CSoundBrowser)
enum { IDD = IDD_SOUNDBROWSER };
CListBox m_SoundList;
CString m_SoundNameSelected;
int m_SoundType;
BOOL m_Autoplay;
CString m_SoundFile;
CString m_SoundSource;
//}}AFX_DATA
// Overrides
// ClassWizard generated virtual function overrides
//{{AFX_VIRTUAL(CSoundBrowser)
public:
virtual int DoModal();
protected:
virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support
//}}AFX_VIRTUAL
void SaveValues();
// Implementation
protected:
// Generated message map functions
//{{AFX_MSG(CSoundBrowser)
virtual BOOL OnInitDialog();
afx_msg void OnClose();
afx_msg void OnChangeFilter();
afx_msg void OnUpdateFilterNOW();
afx_msg void OnSelchangeSoundType();
afx_msg void OnSelchangeSoundList();
afx_msg void OnDblclkSoundList();
afx_msg void OnPreview();
afx_msg void OnAutoplay();
afx_msg void OnBnClickedStopsound();
afx_msg void OnRefreshSounds();
afx_msg void OnTimer(UINT nIDEvent);
afx_msg void OnOpenSource();
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
private:
void Shutdown();
void ClearSoundList();
void PopulateSoundList();
void CopySoundNameToSelected();
SoundType_t GetSoundType() const;
bool ShowSoundInList( const char *pSoundName );
void OnFilterChanged( const char *pFilter );
DWORD m_uLastFilterChange;
BOOL m_bFilterChanged;
BOOL m_bSoundPlayed; // used so we can do a timer query to keep disable the stop sound button
DWORD m_uSoundPlayTime;
int m_nSelectedSoundIndex;
CAutoSelComboBox m_cFilter;
char m_szFilter[256]; // Name filter, space, comma, or semicolon delimited.
int m_nFilters; // The number of names that were parsed out of the name filter.
char *m_Filters[64]; // The individual name filters.
static CStringArray m_FilterHistory;
static int m_nFilterHistory;
};
//{{AFX_INSERT_LOCATION}}
// Microsoft Visual C++ will insert additional declarations immediately before the previous line.
#endif // !defined(AFX_SOUNDBROWSER_H__33046A12_7CF9_4031_AD10_A76200E73280__INCLUDED_)

149
hammer/ToolAxisHandle.cpp Normal file
View File

@@ -0,0 +1,149 @@
//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//=============================================================================//
#include "stdafx.h"
#include "History.h"
#include "MainFrm.h" // For ObjectProperties
#include "MapDoc.h"
#include "MapAxisHandle.h"
#include "MapPointHandle.h"
#include "MapView2D.h"
#include "Render2D.h"
#include "StatusBarIDs.h" // For SetStatusText
#include "ToolManager.h"
#include "ToolAxisHandle.h"
#include "ToolPointHandle.h"
#include "Selection.h"
// memdbgon must be the last include file in a .cpp file!!!
#include <tier0/memdbgon.h>
//-----------------------------------------------------------------------------
// Purpose: Constructor.
//-----------------------------------------------------------------------------
CToolAxisHandle::CToolAxisHandle(void)
{
m_pAxis = NULL;
m_nPointIndex = 0;
}
//-----------------------------------------------------------------------------
// Purpose: Attaches the point to the tool for manipulation.
//-----------------------------------------------------------------------------
void CToolAxisHandle::Attach(CMapAxisHandle *pAxis, int nPointIndex)
{
if ((pAxis != NULL) && (nPointIndex < 2))
{
m_pAxis = pAxis;
m_nPointIndex = nPointIndex;
}
}
//-----------------------------------------------------------------------------
// Purpose: Handles left button down events in the 2D view.
// Input : Per CWnd::OnLButtonDown.
// Output : Returns true if the message was handled, false if not.
//-----------------------------------------------------------------------------
bool CToolAxisHandle::OnLMouseDown2D(CMapView2D *pView, UINT nFlags, const Vector2D &vPoint)
{
//
// Activate this tool and start dragging the axis endpoint.
//
ToolManager()->PushTool(TOOL_AXIS_HANDLE);
pView->SetCapture();
CMapDoc *pDoc = pView->GetMapDoc();
GetHistory()->MarkUndoPosition(pDoc->GetSelection()->GetList(), "Modify Axis");
GetHistory()->Keep(m_pAxis);
return true;
}
//-----------------------------------------------------------------------------
// Purpose: Handles left button up events in the 2D view.
// Input : Per CWnd::OnLButtonUp.
// Output : Returns true if the message was handled, false if not.
//-----------------------------------------------------------------------------
bool CToolAxisHandle::OnLMouseUp2D(CMapView2D *pView, UINT nFlags, const Vector2D &vPoint)
{
// dvsFIXME: do we need to update the point here?
ToolManager()->PopTool();
ReleaseCapture();
CMapDoc *pDoc = pView->GetMapDoc();
pDoc->UpdateAllViews( MAPVIEW_UPDATE_TOOL );
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 CToolAxisHandle::OnMouseMove2D(CMapView2D *pView, UINT nFlags, const Vector2D &vPoint)
{
//
// Make sure the point is visible.
//
pView->ToolScrollToPoint( vPoint);
//
// Snap the point to half the grid size. Do this so that we can always center
// the axis even on odd-width objects.
//
Vector vecWorld;
pView->ClientToWorld(vecWorld, vPoint);
CMapDoc *pDoc = pView->GetMapDoc();
pDoc->Snap(vecWorld, constrainHalfSnap);
//
// Move to the snapped position.
//
Vector vecPos[2];
m_pAxis->GetEndPoint(vecPos[m_nPointIndex], m_nPointIndex);
vecPos[m_nPointIndex][pView->axHorz] = vecWorld[pView->axHorz];
vecPos[m_nPointIndex][pView->axVert] = vecWorld[pView->axVert];
m_pAxis->UpdateEndPoint(vecPos[m_nPointIndex], m_nPointIndex);
int nOtherIndex = (m_nPointIndex == 0);
m_pAxis->GetEndPoint(vecPos[nOtherIndex], nOtherIndex);
//
// Update the status bar and the views.
//
char szBuf[128];
sprintf(szBuf, " (%.0f %.0f %0.f) ", vecPos[m_nPointIndex][0], vecPos[m_nPointIndex][1], vecPos[m_nPointIndex][2]);
SetStatusText(SBI_COORDS, szBuf);
pDoc->UpdateAllViews( MAPVIEW_UPDATE_TOOL );
return true;
}
//-----------------------------------------------------------------------------
// Purpose: Renders the tool in the 2D view.
// Input : pRender - The interface to use for rendering.
//-----------------------------------------------------------------------------
void CToolAxisHandle::RenderTool2D(CRender2D *pRender)
{
SelectionState_t eState = m_pAxis->SetSelectionState(SELECT_MODIFY, m_nPointIndex);
m_pAxis->Render2D(pRender);
m_pAxis->SetSelectionState(eState);
}

47
hammer/ToolAxisHandle.h Normal file
View File

@@ -0,0 +1,47 @@
//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//=============================================================================//
#ifndef TOOLAXISHANDLE_H
#define TOOLAXISHANDLE_H
#ifdef _WIN32
#pragma once
#endif
#include "ToolInterface.h"
class CMapAxisHandle;
class CMapPointHandle;
class CToolAxisHandle : public CBaseTool
{
public:
CToolAxisHandle(void);
void Attach(CMapAxisHandle *pPoint, int nPointIndex);
//
// CBaseTool implementation.
//
virtual ToolID_t GetToolID(void) { return TOOL_AXIS_HANDLE; }
virtual bool OnLMouseDown2D(CMapView2D *pView, UINT nFlags, const Vector2D &vPoint);
virtual bool OnLMouseUp2D(CMapView2D *pView, UINT nFlags, const Vector2D &vPoint);
virtual bool OnMouseMove2D(CMapView2D *pView, UINT nFlags, const Vector2D &vPoint);
virtual void RenderTool2D(CRender2D *pRender);
//virtual void RenderTool3D(CRender3D *pRender);
private:
CMapAxisHandle *m_pAxis; // The axis we are manipulating.
int m_nPointIndex; // The index of the endpoint we are manipulating [0,1].
};
#endif // TOOLAXISHANDLE_H

45
hammer/ToolBase.cpp Normal file
View File

@@ -0,0 +1,45 @@
//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
//=============================================================================//
#include "stdafx.h"
#include "ToolInterface.h"
#include "mapdoc.h"
// memdbgon must be the last include file in a .cpp file!!!
#include <tier0/memdbgon.h>
void CBaseTool::Init( CMapDoc *pDocument )
{
m_bActiveTool = false;
m_pDocument = pDocument;
}
//-----------------------------------------------------------------------------
// Purpose: Called whtn this tool is becoming the active tool.
// Input : eOldTool - The tool that was previously active.
//-----------------------------------------------------------------------------
void CBaseTool::Activate()
{
OnActivate();
m_pDocument->UpdateAllViews( MAPVIEW_UPDATE_TOOL );
m_bActiveTool = true;
}
//-----------------------------------------------------------------------------
// Purpose: Called when this tool is no longer the active tool.
// Input : eNewTool - The tool that is being activated.
//-----------------------------------------------------------------------------
void CBaseTool::Deactivate()
{
OnDeactivate();
if ( m_pDocument )
m_pDocument->UpdateAllViews( MAPVIEW_UPDATE_TOOL );
m_bActiveTool = false;
}

518
hammer/ToolBlock.cpp Normal file
View File

@@ -0,0 +1,518 @@
//========= Copyright © 1996-2005, 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.
Vector bmins,bmaxs;
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();
}

57
hammer/ToolBlock.h Normal file
View File

@@ -0,0 +1,57 @@
//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//=============================================================================//
#ifndef TOOLBLOCK_H
#define TOOLBLOCK_H
#ifdef _WIN32
#pragma once
#endif
#include "Box3D.h"
#include "ToolInterface.h"
class CToolBlockMessageWnd;
class CToolBlock : public Box3D
{
friend class CToolBlockMessageWnd;
public:
CToolBlock();
~CToolBlock();
//
// CBaseTool implementation.
//
virtual ToolID_t GetToolID(void) { return TOOL_BLOCK; }
virtual bool OnContextMenu2D(CMapView2D *pView, UINT nFlags, const Vector2D &vPoint);
virtual bool OnKeyDown2D(CMapView2D *pView, UINT nChar, UINT nRepCnt, UINT nFlags);
virtual bool OnLMouseDown2D(CMapView2D *pView, UINT nFlags, const Vector2D &vPoint);
virtual bool OnLMouseDown3D(CMapView3D *pView, UINT nFlags, const Vector2D &vPoint) ;
virtual bool OnLMouseUp2D(CMapView2D *pView, UINT nFlags, const Vector2D &vPoint);
virtual bool OnMouseMove2D(CMapView2D *pView, UINT nFlags, const Vector2D &vPoint);
virtual bool OnMouseMove3D(CMapView3D *pView, UINT nFlags, const Vector2D &vPoint);
virtual bool OnLMouseUp3D(CMapView3D *pView, UINT nFlags, const Vector2D &vPoint);
virtual bool OnKeyDown3D(CMapView3D *pView, UINT nChar, UINT nRepCnt, UINT nFlags);
private:
void CreateMapObject(CMapView *pView);
void OnEscape();
void SetBlockCursor();
};
#endif // TOOLBLOCK_H

816
hammer/ToolCamera.cpp Normal file
View File

@@ -0,0 +1,816 @@
//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//=============================================================================//
#include "stdafx.h"
#include "ToolCamera.h"
#include "SaveInfo.h"
#include "MainFrm.h" // dvs: remove?
#include "MapDefs.h"
#include "MapDoc.h"
#include "MapView2D.h"
#include "MapView3D.h"
#include "Options.h"
#include "Render2D.h"
#include "StatusBarIDs.h" // dvs: remove
#include "ToolManager.h"
#include "hammer_mathlib.h"
#include "vgui/Cursor.h"
#include "Selection.h"
// memdbgon must be the last include file in a .cpp file!!!
#include <tier0/memdbgon.h>
#pragma warning(disable:4244)
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
Camera3D::Camera3D(void)
{
Cameras.EnsureCapacity(16);
SetEmpty();
}
//-----------------------------------------------------------------------------
// Purpose: Returns true if we are dragging a camera, false if not. // dvs: rename
//-----------------------------------------------------------------------------
bool Camera3D::IsEmpty(void)
{
return (Cameras.Count() == 0);
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void Camera3D::SetEmpty(void)
{
Cameras.RemoveAll();
m_iActiveCamera = -1;
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : pt -
// BOOL -
// Output : int
//-----------------------------------------------------------------------------
int Camera3D::HitTest(CMapView *pView, const Vector2D &ptClient, bool bTestHandles)
{
for(int i = 0; i < Cameras.Count(); i++)
{
for ( int j=0; j<2; j++ )
{
if( HitRect( pView, ptClient, Cameras[i].position[j], HANDLE_RADIUS ) )
{
return MAKELONG(i+1, j);
}
}
}
return FALSE;
}
//-----------------------------------------------------------------------------
// Purpose: Get rid of extra cameras if we have too many.
//-----------------------------------------------------------------------------
void Camera3D::EnsureMaxCameras()
{
int nMax = max( Options.general.nMaxCameras, 1 );
int nToRemove = Cameras.Count() - nMax;
if ( nToRemove > 0 )
{
m_iActiveCamera = max( m_iActiveCamera - nToRemove, 0 );
while ( nToRemove-- )
Cameras.Remove( 0 );
}
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : bSave -
//-----------------------------------------------------------------------------
void Camera3D::FinishTranslation(bool bSave)
{
if (bSave)
{
if ( m_iActiveCamera == Cameras.Count() )
{
Cameras.AddToTail();
EnsureMaxCameras();
}
Cameras[m_iActiveCamera] = m_MoveCamera;
}
Tool3D::FinishTranslation(bSave);
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : pt -
// uFlags -
// CSize& -
// Output : Returns TRUE on success, FALSE on failure.
//-----------------------------------------------------------------------------
bool Camera3D::UpdateTranslation(const Vector &vUpdate, UINT uFlags)
{
Vector vCamDelta = m_MoveCamera.position[1] - m_MoveCamera.position[0];
Vector vNewPos = m_vOrgPos + vUpdate;
// snap point if need be
if ( uFlags & constrainSnap )
m_pDocument->Snap( vNewPos, uFlags );
m_MoveCamera.position[m_nMovePositionIndex] = vNewPos;
if(uFlags & constrainMoveAll)
{
m_MoveCamera.position[(m_nMovePositionIndex+1)%2] = vNewPos + vCamDelta;
}
m_pDocument->UpdateAllViews( MAPVIEW_UPDATE_TOOL );
return true;
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : pCamPos -
// iCamera -
//-----------------------------------------------------------------------------
void Camera3D::GetCameraPos(Vector &vViewPos, Vector &vLookAt)
{
if(!inrange(m_iActiveCamera, 0, Cameras.Count()))
{
vViewPos.Init();
vLookAt.Init();
return;
}
vViewPos = Cameras[m_iActiveCamera].position[0];
vLookAt = Cameras[m_iActiveCamera].position[1];
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : pCamPos -
// iCamera -
//-----------------------------------------------------------------------------
void Camera3D::AddCamera(CAMSTRUCT &camera)
{
Cameras.AddToTail( camera );
EnsureMaxCameras();
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : pRender -
//-----------------------------------------------------------------------------
void Camera3D::RenderTool2D(CRender2D *pRender)
{
for (int i = 0; i < Cameras.Count(); i++)
{
CAMSTRUCT *pDrawCam = &Cameras[i];
if (IsTranslating() && (i == m_iActiveCamera))
{
pDrawCam = &m_MoveCamera;
}
//
// Draw the line between.
//
if (i == m_iActiveCamera)
{
pRender->SetDrawColor( 255, 0, 0 );
}
else
{
pRender->SetDrawColor( 0, 255, 255 );
}
pRender->DrawLine( pDrawCam->position[MovePos], pDrawCam->position[MoveLook] );
//
// Draw camera handle.
//
pRender->SetHandleStyle(HANDLE_RADIUS, CRender::HANDLE_CIRCLE );
pRender->SetHandleColor( 0, 255, 255 );
pRender->DrawHandle( pDrawCam->position[MovePos] );
}
}
//-----------------------------------------------------------------------------
// Purpose: Handles key values being read from the MAP file.
// Input : szKey - Key being loaded.
// szValue - Value of the key being loaded.
// pCam - Camera structure to place the values into.
// Output : Returns ChunkFile_Ok to indicate success.
//-----------------------------------------------------------------------------
ChunkFileResult_t Camera3D::LoadCameraKeyCallback(const char *szKey, const char *szValue, CAMSTRUCT *pCam)
{
if (!stricmp(szKey, "look"))
{
CChunkFile::ReadKeyValueVector3(szValue, pCam->position[MoveLook]);
}
else if (!stricmp(szKey, "position"))
{
CChunkFile::ReadKeyValueVector3(szValue, pCam->position[MovePos]);
}
return(ChunkFile_Ok);
}
//-----------------------------------------------------------------------------
// Purpose: Handles key values being read from the MAP file.
// Input : szKey - Key being loaded.
// szValue - Value of the key being loaded.
// pCam - Camera structure to place the values into.
// Output : Returns ChunkFile_Ok to indicate success.
//-----------------------------------------------------------------------------
ChunkFileResult_t Camera3D::LoadCamerasKeyCallback(const char *szKey, const char *szValue, Camera3D *pCameras)
{
if (!stricmp(szKey, "activecamera"))
{
pCameras->m_iActiveCamera = atoi(szValue);
}
return(ChunkFile_Ok);
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : *pLoadInfo -
// *pSolid -
// Output : ChunkFileResult_t
//-----------------------------------------------------------------------------
ChunkFileResult_t Camera3D::LoadCameraCallback(CChunkFile *pFile, Camera3D *pCameras)
{
CAMSTRUCT Cam;
memset(&Cam, 0, sizeof(Cam));
ChunkFileResult_t eResult = pFile->ReadChunk((KeyHandler_t)LoadCameraKeyCallback, &Cam);
if (eResult == ChunkFile_Ok)
{
pCameras->AddCamera( Cam );
}
return(eResult);
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : *pFile -
// Output : ChunkFileResult_t
//-----------------------------------------------------------------------------
ChunkFileResult_t Camera3D::LoadVMF(CChunkFile *pFile)
{
//
// Set up handlers for the subchunks that we are interested in.
//
CChunkHandlerMap Handlers;
Handlers.AddHandler("camera", (ChunkHandler_t)LoadCameraCallback, this);
pFile->PushHandlers(&Handlers);
ChunkFileResult_t eResult = pFile->ReadChunk((KeyHandler_t)LoadCamerasKeyCallback, this);
pFile->PopHandlers();
if (eResult == ChunkFile_Ok)
{
//
// Make sure the active camera is legal.
//
if (Cameras.Count() == 0)
{
m_iActiveCamera = -1;
}
else if (!inrange(m_iActiveCamera, 0, Cameras.Count()))
{
m_iActiveCamera = 0;
}
}
return(eResult);
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : &dir -
// &pos -
//-----------------------------------------------------------------------------
void Camera3D::UpdateActiveCamera(Vector &vViewPos, Vector &vDir)
{
if(!inrange(m_iActiveCamera, 0, Cameras.Count()))
return;
Vector& camPos = Cameras[m_iActiveCamera].position[MovePos];
Vector& lookPos = Cameras[m_iActiveCamera].position[MoveLook];
// get current length
Vector delta;
for(int i = 0; i < 3; i++)
delta[i] = camPos[i] - lookPos[i];
float length = VectorLength(delta);
if ( length < 1 )
length = 1;
camPos = vViewPos;
for(int i = 0; i < 3; i++)
lookPos[i] = camPos[i] + vDir[i] * length;
if ( IsActiveTool() )
{
if (Options.view2d.bCenteroncamera)
{
VIEW2DINFO vi;
vi.wFlags = VI_CENTER;
vi.ptCenter = vViewPos;
m_pDocument->SetView2dInfo(vi);
}
m_pDocument->UpdateAllViews( MAPVIEW_UPDATE_TOOL );
}
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : type -
//-----------------------------------------------------------------------------
void Camera3D::SetNextCamera(SNCTYPE type)
{
if(Cameras.Count()==0)
{
m_iActiveCamera = -1;
return;
}
switch(type)
{
case sncNext:
++m_iActiveCamera;
if(m_iActiveCamera >= Cameras.Count() )
m_iActiveCamera = 0;
break;
case sncPrev:
--m_iActiveCamera;
if(m_iActiveCamera < 0)
m_iActiveCamera = Cameras.Count()-1;
break;
case sncFirst:
m_iActiveCamera = 0;
break;
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void Camera3D::DeleteActiveCamera()
{
if(!inrange(m_iActiveCamera, 0, Cameras.Count()))
return;
Cameras.Remove(m_iActiveCamera);
if(m_iActiveCamera >= Cameras.Count() )
m_iActiveCamera = Cameras.Count()-1;
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : file -
// fIsStoring -
//-----------------------------------------------------------------------------
void Camera3D::SerializeRMF(std::fstream& file, BOOL fIsStoring)
{
float fVersion = 0.2f, fThisVersion;
int nCameras = Cameras.Count();
if(fIsStoring)
{
file.write((char*)&fVersion, sizeof(fVersion) );
file.write((char*)&m_iActiveCamera, sizeof(m_iActiveCamera) );
file.write((char*)&nCameras, sizeof(nCameras));
for(int i = 0; i < nCameras; i++)
{
file.write((char*)&Cameras[i], sizeof(CAMSTRUCT));
}
}
else
{
file.read((char*)&fThisVersion, sizeof(fThisVersion) );
if(fThisVersion >= 0.2f)
{
file.read((char*)&m_iActiveCamera, sizeof(m_iActiveCamera));
}
file.read((char*)&nCameras, sizeof (nCameras) );
Cameras.RemoveAll();
Cameras.EnsureCapacity(nCameras);
for(int i = 0; i < nCameras; i++)
{
CAMSTRUCT cam;
file.read((char*)&cam, sizeof(CAMSTRUCT));
Cameras.AddToTail( cam );
}
EnsureMaxCameras();
Assert( Cameras.Count() == nCameras );
}
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : *pFile -
// Output : ChunkFileResult_t
//-----------------------------------------------------------------------------
ChunkFileResult_t Camera3D::SaveVMF(CChunkFile *pFile, CSaveInfo *pSaveInfo)
{
ChunkFileResult_t eResult = pFile->BeginChunk( GetVMFChunkName() );
if (eResult == ChunkFile_Ok)
{
eResult = pFile->WriteKeyValueInt("activecamera", m_iActiveCamera);
}
if (eResult == ChunkFile_Ok)
{
for (int i = 0; i < Cameras.Count(); i++)
{
eResult = pFile->BeginChunk("camera");
if (eResult == ChunkFile_Ok)
{
eResult = pFile->WriteKeyValueVector3("position", Cameras[i].position[MovePos]);
}
if (eResult == ChunkFile_Ok)
{
eResult = pFile->WriteKeyValueVector3("look", Cameras[i].position[MoveLook]);
}
if (eResult == ChunkFile_Ok)
{
eResult = pFile->EndChunk();
}
if (eResult != ChunkFile_Ok)
{
break;
}
}
}
if (eResult == ChunkFile_Ok)
{
eResult = pFile->EndChunk();
}
return(eResult);
}
//-----------------------------------------------------------------------------
// Purpose: Handles the key down event in the 2D view.
// Input : Per CWnd::OnKeyDown.
// Output : Returns true if the message was handled, false if not.
//-----------------------------------------------------------------------------
bool Camera3D::OnKeyDown2D(CMapView2D *pView, UINT nChar, UINT nRepCnt, UINT nFlags)
{
if (nChar == VK_DELETE || nChar == VK_NEXT || nChar == VK_PRIOR)
{
CMapDoc *pDoc = pView->GetMapDoc();
if (nChar == VK_DELETE)
{
DeleteActiveCamera();
}
else if (nChar == VK_NEXT)
{
SetNextCamera(Camera3D::sncNext);
}
else
{
SetNextCamera(Camera3D::sncPrev);
}
Vector viewPos,lookAt;
GetCameraPos( viewPos, lookAt );
pDoc->UpdateAllCameras( &viewPos, &lookAt, NULL );
return true;
}
else if (nChar == VK_ESCAPE)
{
OnEscape();
return true;
}
return false;
}
//-----------------------------------------------------------------------------
// Purpose: Handles the left mouse button down event in the 2D view.
// Input : Per CWnd::OnLButtonDown.
// Output : Returns true if the message was handled, false if not.
//-----------------------------------------------------------------------------
bool Camera3D::OnLMouseDown2D(CMapView2D *pView, UINT nFlags, const Vector2D &vPoint)
{
CMapDoc *pDoc = pView->GetMapDoc();
pView->SetCapture();
//
// If there are no cameras created yet or they are holding down
// the SHIFT key, create a new camera now.
//
Vector vecWorld;
pView->ClientToWorld( vecWorld, vPoint );
if ( IsEmpty() || (nFlags & MK_SHIFT))
{
//
// Build a point in world space to place the new camera.
//
if ( !pDoc->GetSelection()->IsEmpty() )
{
Vector vecCenter;
pDoc->GetSelection()->GetBoundsCenter(vecCenter);
vecWorld[pView->axThird] = vecCenter[pView->axThird];
}
else
{
vecWorld[pView->axThird] = COORD_NOTINIT;
pDoc->GetBestVisiblePoint(vecWorld);
}
//
// Create a new camera.
//
m_vOrgPos = vecWorld;
m_MoveCamera.position[MovePos] = vecWorld;
m_MoveCamera.position[MoveLook] = vecWorld;
m_nMovePositionIndex = MoveLook;
// set as active camera
m_iActiveCamera = Cameras.AddToTail(m_MoveCamera);;
EnsureMaxCameras();
StartTranslation(pView, vPoint );
}
//
// Otherwise, try to drag an existing camera handle.
//
else
{
int dwHit = HitTest( pView, vPoint );
if ( dwHit )
{
m_iActiveCamera = LOWORD(dwHit)-1;
m_MoveCamera = Cameras[m_iActiveCamera];
m_nMovePositionIndex = HIWORD(dwHit);
m_vOrgPos = m_MoveCamera.position[m_nMovePositionIndex];
StartTranslation( pView, vPoint );
}
}
return true;
}
//-----------------------------------------------------------------------------
// Purpose: Handles the left mouse button up event in the 2D view.
// Input : Per CWnd::OnLButtonUp.
// Output : Returns true if the message was handled, false if not.
//-----------------------------------------------------------------------------
bool Camera3D::OnLMouseUp2D(CMapView2D *pView, UINT nFlags, const Vector2D &vPoint)
{
ReleaseCapture();
if (IsTranslating())
{
FinishTranslation(true);
Vector viewPos, lookAt;
GetCameraPos( viewPos, lookAt );
m_pDocument->UpdateAllCameras( &viewPos, &lookAt, NULL );
}
m_pDocument->UpdateStatusbar();
return true;
}
unsigned int Camera3D::GetConstraints(unsigned int nKeyFlags)
{
unsigned int uConstraints = Tool3D::GetConstraints( nKeyFlags );
if(nKeyFlags & MK_CONTROL)
{
uConstraints |= constrainMoveAll;
}
return uConstraints;
}
//-----------------------------------------------------------------------------
// Purpose: Handles the mouse move event in the 2D view.
// Input : Per CWnd::OnMouseMove.
// Output : Returns true if the message was handled, false if not.
//-----------------------------------------------------------------------------
bool Camera3D::OnMouseMove2D(CMapView2D *pView, UINT nFlags, const Vector2D &vPoint)
{
CMapDoc *pDoc = pView->GetMapDoc();
if (!pDoc)
{
return true;
}
vgui::HCursor hCursor = vgui::dc_arrow;
unsigned int uConstraints = GetConstraints( nFlags );
// Make sure the point is visible.
pView->ToolScrollToPoint( vPoint );
//
// Convert to world coords.
//
Vector vecWorld;
pView->ClientToWorld(vecWorld, vPoint);
//
// Update status bar position display.
//
char szBuf[128];
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 ( !IsEmpty() )
{
//
// If the cursor is on a handle, set it to a cross.
//
if ( HitTest( pView, vPoint, true) )
{
hCursor = vgui::dc_crosshair;
}
}
if ( hCursor != vgui::dc_none )
pView->SetCursor( hCursor );
return true;
}
//-----------------------------------------------------------------------------
// Purpose: Handles the left mouse button down event in the 3D view.
// Input : Per CWnd::OnLButtonDown.
// Output : Returns true if the message was handled, false if not.
//-----------------------------------------------------------------------------
bool Camera3D::OnLMouseDown3D(CMapView3D *pView, UINT nFlags, const Vector2D &vPoint)
{
pView->EnableRotating(true);
return true;
}
//-----------------------------------------------------------------------------
// Purpose: Handles the left mouse up down event in the 3D view.
// Input : Per CWnd::OnLButtonUp.
// Output : Returns true if the message was handled, false if not.
//-----------------------------------------------------------------------------
bool Camera3D::OnLMouseUp3D(CMapView3D *pView, UINT nFlags, const Vector2D &vPoint)
{
pView->EnableRotating(false);
pView->UpdateCameraVariables();
return true;
}
//-----------------------------------------------------------------------------
// Purpose: Handles the right mouse button down event in the 3D view.
// Input : Per CWnd::OnRButtonDown.
// Output : Returns true if the message was handled, false if not.
//-----------------------------------------------------------------------------
bool Camera3D::OnRMouseDown3D(CMapView3D *pView, UINT nFlags, const Vector2D &vPoint)
{
pView->EnableStrafing(true);
return true;
}
//-----------------------------------------------------------------------------
// Purpose: Handles the right mouse button up event in the 3D view.
// Input : Per CWnd::OnRButtonUp.
// Output : Returns true if the message was handled, false if not.
//-----------------------------------------------------------------------------
bool Camera3D::OnRMouseUp3D(CMapView3D *pView, UINT nFlags, const Vector2D &vPoint)
{
pView->EnableStrafing(false);
pView->UpdateCameraVariables();
return true;
}
//-----------------------------------------------------------------------------
// Purpose: Handles the key down event in the 3D view.
// Input : Per CWnd::OnKeyDown.
// Output : Returns true if the message was handled, false if not.
//-----------------------------------------------------------------------------
bool Camera3D::OnKeyDown3D(CMapView3D *pView, UINT nChar, UINT nRepCnt, UINT nFlags)
{
if (nChar == VK_DELETE || nChar == VK_NEXT || nChar == VK_PRIOR)
{
CMapDoc *pDoc = pView->GetMapDoc();
if (nChar == VK_DELETE)
{
DeleteActiveCamera();
}
else if (nChar == VK_NEXT)
{
SetNextCamera(Camera3D::sncNext);
}
else
{
SetNextCamera(Camera3D::sncPrev);
}
Vector viewPos, lookAt;
GetCameraPos( viewPos, lookAt );
pDoc->UpdateAllCameras( &viewPos, &lookAt, NULL );
return true;
}
else if (nChar == VK_ESCAPE)
{
OnEscape();
return true;
}
return false;
}
//-----------------------------------------------------------------------------
// Purpose: Handles the escape key in the 2D or 3D views.
//-----------------------------------------------------------------------------
void Camera3D::OnEscape(void)
{
//
// Stop using the camera tool.
//
m_pDocument->GetTools()->SetTool(TOOL_POINTER);
}

132
hammer/ToolCamera.h Normal file
View File

@@ -0,0 +1,132 @@
//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//=============================================================================//
#ifndef CAMERA3D_H
#define CAMERA3D_H
#pragma once
#include "Tool3D.h"
#include "ToolInterface.h"
#include "utlvector.h"
#pragma warning(push, 1)
#pragma warning(disable:4701 4702 4530)
#include <fstream>
#pragma warning(pop)
class CChunkFile;
class CSaveInfo;
enum ChunkFileResult_t;
//
// Defines a camera position/look pair.
//
struct CAMSTRUCT
{
// index 0 = camera origin, 1 = pos look to
Vector position[2];
};
class Camera3D : public Tool3D
{
public:
Camera3D(void);
enum SNCTYPE
{
sncNext = -1,
sncFirst = 0,
sncPrev = 1
};
int GetActiveCamera(void) { return m_iActiveCamera; }
void GetCameraPos(Vector &vViewPos, Vector &vLookAt);
void UpdateActiveCamera(Vector &vViewPos, Vector &vLookAt);
//
// Serialization.
//
const char *GetVMFChunkName() { return "cameras"; }
ChunkFileResult_t LoadVMF(CChunkFile *pFile);
ChunkFileResult_t SaveVMF(CChunkFile *pFile, CSaveInfo *pSaveInfo);
void SerializeRMF(std::fstream &file, BOOL fIsStoring);
//
// Tool3D implementation.
//
virtual bool IsEmpty(void);
virtual void SetEmpty(void);
virtual unsigned int GetConstraints(unsigned int nKeyFlags);
//
// CBaseTool implementation.
//
virtual ToolID_t GetToolID(void) { return TOOL_CAMERA; }
virtual bool OnKeyDown2D(CMapView2D *pView, UINT nChar, UINT nRepCnt, UINT nFlags);
virtual bool OnLMouseDown2D(CMapView2D *pView, UINT nFlags, const Vector2D &vPoint);
virtual bool OnLMouseUp2D(CMapView2D *pView, UINT nFlags, const Vector2D &vPoint);
virtual bool OnMouseMove2D(CMapView2D *pView, UINT nFlags, const Vector2D &vPoint);
virtual bool OnLMouseDown3D(CMapView3D *pView, UINT nFlags, const Vector2D &vPoint);
virtual bool OnLMouseUp3D(CMapView3D *pView, UINT nFlags, const Vector2D &vPoint);
virtual bool OnRMouseDown3D(CMapView3D *pView, UINT nFlags, const Vector2D &vPoint);
virtual bool OnRMouseUp3D(CMapView3D *pView, UINT nFlags, const Vector2D &vPoint);
virtual bool OnKeyDown3D(CMapView3D *pView, UINT nChar, UINT nRepCnt, UINT nFlags);
virtual void RenderTool2D(CRender2D *pRender);
protected:
//
// Tool3D implementation.
//
virtual int HitTest(CMapView *pView, const Vector2D &vPoint, bool bTestHandles = false);
virtual bool UpdateTranslation(const Vector &vUpdate, UINT flags = 0);
virtual void FinishTranslation(bool bSave);
private:
int GetCameraCount() { return Cameras.Count(); }
void AddCamera(CAMSTRUCT &pCamPos);
void SetNextCamera(SNCTYPE next);
void DeleteActiveCamera(void);
void OnEscape(void);
void EnsureMaxCameras();
static ChunkFileResult_t LoadCameraKeyCallback(const char *szKey, const char *szValue, CAMSTRUCT *pCam);
static ChunkFileResult_t LoadCamerasKeyCallback(const char *szKey, const char *szValue, Camera3D *pCameras);
static ChunkFileResult_t LoadCameraCallback(CChunkFile *pFile, Camera3D *pCameras);
CUtlVector<CAMSTRUCT> Cameras; // The cameras that have been created.
CAMSTRUCT m_MoveCamera;
enum
{
MovePos = 0,
MoveLook = 1,
};
int m_iActiveCamera;
int m_nMovePositionIndex;
Vector m_vOrgPos;
};
#endif // CAMERA3D_H

987
hammer/ToolClipper.cpp Normal file
View File

@@ -0,0 +1,987 @@
//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//=============================================================================//
#include "stdafx.h"
#include "GlobalFunctions.h"
#include "History.h"
#include "MapDefs.h"
#include "MapDoc.h"
#include "MapFace.h"
#include "MapSolid.h"
#include "MapView2D.h"
#include "MapWorld.h"
#include "Options.h"
#include "Render2D.h"
#include "Render3D.h"
#include "RenderUtils.h"
#include "StatusBarIDs.h" // dvs: remove
#include "ToolClipper.h"
#include "ToolManager.h"
#include "vgui/Cursor.h"
#include "Selection.h"
// memdbgon must be the last include file in a .cpp file!!!
#include <tier0/memdbgon.h>
#pragma warning( disable:4244 )
//=============================================================================
//
// Friend Function (for MapClass->EnumChildren Callback)
//
//-----------------------------------------------------------------------------
// Purpose: This function creates a new clip group with the given solid as
// the original solid.
// Input: pSolid - the original solid to put in the clip list
// pClipper - the clipper tool
// Output: successful?? (true/false)
//-----------------------------------------------------------------------------
BOOL AddToClipList( CMapSolid *pSolid, Clipper3D *pClipper )
{
CClipGroup *pClipGroup = new CClipGroup;
if( !pClipGroup )
return false;
pClipGroup->SetOrigSolid( pSolid );
pClipper->m_ClipResults.AddToTail( pClipGroup );
return true;
}
//=============================================================================
//
// CClipGroup
//
//-----------------------------------------------------------------------------
// Purpose: Destructor. Gets rid of the unnecessary clip solids.
//-----------------------------------------------------------------------------
CClipGroup::~CClipGroup()
{
delete m_pClipSolids[0];
delete m_pClipSolids[1];
}
//-----------------------------------------------------------------------------
// Purpose: constructor - initialize the clipper variables
//-----------------------------------------------------------------------------
Clipper3D::Clipper3D(void)
{
m_Mode = FRONT;
m_ClipPlane.normal.Init();
m_ClipPlane.dist = 0.0f;
m_ClipPoints[0].Init();
m_ClipPoints[1].Init();
m_ClipPointHit = -1;
m_pOrigObjects = NULL;
m_bDrawMeasurements = false;
SetEmpty();
}
//-----------------------------------------------------------------------------
// Purpose: deconstructor
//-----------------------------------------------------------------------------
Clipper3D::~Clipper3D(void)
{
}
//-----------------------------------------------------------------------------
// Purpose: Called when the tool is activated.
// Input : eOldTool - The ID of the previously active tool.
//-----------------------------------------------------------------------------
void Clipper3D::OnActivate()
{
if (IsActiveTool())
{
//
// Already the active tool - toggle the mode.
//
IterateClipMode();
}
}
//-----------------------------------------------------------------------------
// Purpose: Called when the tool is deactivated.
// Input : eNewTool - The ID of the tool that is being activated.
//-----------------------------------------------------------------------------
void Clipper3D::OnDeactivate()
{
SetEmpty();
}
//-----------------------------------------------------------------------------
// Purpose: (virtual imp) This function handles the "dragging" of the mouse
// while the left mouse button is depressed. It updates the position
// of the clippoing plane point selected in the StartTranslation
// function. This function rebuilds the clipping plane and updates
// the clipping solids when necessary.
// Input: pt - current location of the mouse in the 2DView
// uFlags - constrained clipping plane point movement
// *dragSize - not used in the virtual implementation
// Output: success of translation (TRUE/FALSE)
//-----------------------------------------------------------------------------
bool Clipper3D::UpdateTranslation( const Vector &vUpdate, UINT uFlags )
{
// sanity check
if( IsEmpty() )
return false;
Vector vNewPos = m_vOrgPos + vUpdate;
// snap point if need be
if ( uFlags & constrainSnap )
m_pDocument->Snap( vNewPos, uFlags );
//
// update clipping point positions
//
if ( m_ClipPoints[m_ClipPointHit] == vNewPos )
return false;
if( uFlags & constrainMoveAll )
{
//
// calculate the point and delta - to move both clip points simultaneously
//
Vector delta = vNewPos - m_ClipPoints[m_ClipPointHit];
m_ClipPoints[(m_ClipPointHit+1)%2] += delta;
}
m_ClipPoints[m_ClipPointHit] = vNewPos;
// build the new clip plane and update clip results
BuildClipPlane();
GetClipResults();
m_pDocument->UpdateAllViews( MAPVIEW_UPDATE_TOOL );
return true;
}
//-----------------------------------------------------------------------------
// Purpose: (virtual imp) This function defines all finishing functionality
// necessary at the end of a clipping action. Nothing really!!!
// Input : bSave - passed along the the Tool finish translation call
//-----------------------------------------------------------------------------
void Clipper3D::FinishTranslation( bool bSave )
{
// get the clip results -- in case the update is a click and not a drag
GetClipResults();
Tool3D::FinishTranslation( bSave );
}
//-----------------------------------------------------------------------------
// Purpose: iterate through the types of clipping modes, update after an
// iteration takes place to visualize the new clip results
//-----------------------------------------------------------------------------
void Clipper3D::IterateClipMode( void )
{
//
// increment the clipping mode (wrap when necessary)
//
m_Mode++;
if( m_Mode > BOTH )
{
m_Mode = FRONT;
}
// update the clipped objects based on the mode
GetClipResults();
}
//-----------------------------------------------------------------------------
// Purpose: This resets the solids to clip (the original list) and calls the
// CalcClipResults function to generate new "clip" solids
//-----------------------------------------------------------------------------
void Clipper3D::GetClipResults( void )
{
// reset the clip list to the original solid lsit
SetClipObjects( m_pOrigObjects );
// calculate the clipped objects based on the current "clip plane"
CalcClipResults();
}
//-----------------------------------------------------------------------------
// Purpose: This function allows one to specifically set the clipping plane
// information, as opposed to building a clip plane during "translation"
// Input: pPlane - the plane information used to create the clip plane
//-----------------------------------------------------------------------------
void Clipper3D::SetClipPlane( PLANE *pPlane )
{
//
// copy the clipping plane info
//
m_ClipPlane.normal = pPlane->normal;
m_ClipPlane.dist = pPlane->dist;
}
//-----------------------------------------------------------------------------
// Purpose: This function builds a clipping plane based on the clip point
// locations manipulated in the "translation" functions and the 2DView
//-----------------------------------------------------------------------------
void Clipper3D::BuildClipPlane( void )
{
// calculate the up vector
Vector upVect = m_vPlaneNormal;
// calculate the right vector
Vector rightVect;
VectorSubtract( m_ClipPoints[1], m_ClipPoints[0], rightVect );
// calculate the forward (normal) vector
Vector forwardVect;
CrossProduct( upVect, rightVect, forwardVect );
VectorNormalize( forwardVect );
//
// save the clip plane info
//
m_ClipPlane.normal = forwardVect;
m_ClipPlane.dist = DotProduct( m_ClipPoints[0], forwardVect );
}
//-----------------------------------------------------------------------------
// Purpose: This functions sets up the list of objects to be clipped.
// Initially the list is passed in (typically a Selection set). On
// subsequent "translation" updates the list is refreshed from the
// m_pOrigObjects list.
// Input: pList - the list of objects (solids) to be clipped
//-----------------------------------------------------------------------------
void Clipper3D::SetClipObjects( const CMapObjectList *pList )
{
// check for an empty list
if( !pList )
return;
// save the original list
m_pOrigObjects = pList;
// clear the clip results list
ResetClipResults();
//
// copy solids into the clip list
//
FOR_EACH_OBJ( *m_pOrigObjects, pos )
{
CMapClass *pObject = (CUtlReference< CMapClass >)m_pOrigObjects->Element( pos );
if( !pObject )
continue;
if( pObject->IsMapClass( MAPCLASS_TYPE( CMapSolid ) ) )
{
AddToClipList( ( CMapSolid* )pObject, this );
}
pObject->EnumChildren( ENUMMAPCHILDRENPROC( AddToClipList ), DWORD( this ), MAPCLASS_TYPE( CMapSolid ) );
}
// the clipping list is not empty anymore
m_bEmpty = false;
}
//-----------------------------------------------------------------------------
// Purpose: This function calculates based on the defined or given clipping
// plane and clipping mode the new clip solids.
//-----------------------------------------------------------------------------
void Clipper3D::CalcClipResults( void )
{
// sanity check
if( IsEmpty() )
return;
//
// iterate through and clip all of the solids in the clip list
//
FOR_EACH_OBJ( m_ClipResults, pos )
{
CClipGroup *pClipGroup = m_ClipResults.Element( pos );
CMapSolid *pOrigSolid = pClipGroup->GetOrigSolid();
if( !pOrigSolid )
continue;
//
// check the modes for which solids to generate
//
CMapSolid *pFront = NULL;
CMapSolid *pBack = NULL;
if( m_Mode == FRONT )
{
pOrigSolid->Split( &m_ClipPlane, &pFront, NULL );
}
else if( m_Mode == BACK )
{
pOrigSolid->Split( &m_ClipPlane, NULL, &pBack );
}
else if( m_Mode == BOTH )
{
pOrigSolid->Split( &m_ClipPlane, &pFront, &pBack );
}
if( pFront )
{
pFront->SetTemporary(true);
pClipGroup->SetClipSolid( pFront, FRONT );
}
if( pBack )
{
pBack->SetTemporary(true);
pClipGroup->SetClipSolid( pBack, BACK );
}
}
}
//-----------------------------------------------------------------------------
// Purpose: This function handles the removal of the "original" solid when it
// has been clipped into new solid(s) or removed from the world (group
// or entity) entirely. It handles this in an undo safe fashion.
// Input: pOrigSolid - the solid to remove
//-----------------------------------------------------------------------------
void Clipper3D::RemoveOrigSolid( CMapSolid *pOrigSolid )
{
m_pDocument->DeleteObject(pOrigSolid);
//
// remove the solid from the selection set if in the seleciton set and
// its parent is the world, or set the selection state to none parent is group
// or entity in the selection set
//
CSelection *pSelection = m_pDocument->GetSelection();
if ( pSelection->IsSelected( pOrigSolid ) )
{
pSelection->SelectObject( pOrigSolid, scUnselect );
}
else
{
pOrigSolid->SetSelectionState( SELECT_NONE );
}
}
//-----------------------------------------------------------------------------
// Purpose: This function handles the saving of newly clipped solids (derived
// from an "original" solid). It handles them in an undo safe fashion.
// Input: pSolid - the newly clipped solid
// pOrigSolid - the "original" solid or solid the clipped solid was
// derived from
//-----------------------------------------------------------------------------
void Clipper3D::SaveClipSolid( CMapSolid *pSolid, CMapSolid *pOrigSolid )
{
//
// no longer a temporary solid
//
pSolid->SetTemporary( FALSE );
//
// Add the new solid to the original solid's parent (group, entity, world, etc.).
//
m_pDocument->AddObjectToWorld(pSolid, pOrigSolid->GetParent());
//
// handle linking solid into selection -- via selection set when parent is the world
// and selected, or set the selection state if parent is group or entity in selection set
//
if( m_pDocument->GetSelection()->IsSelected( pOrigSolid ) )
{
m_pDocument->SelectObject( pSolid, scSelect );
}
else
{
pSolid->SetSelectionState( SELECT_NORMAL );
}
GetHistory()->KeepNew( pSolid );
}
//-----------------------------------------------------------------------------
// Purpose: This function saves all the clipped solid information. If new solids
// were generated from the original, they are saved and the original is
// set for desctruciton. Otherwise, the original solid is kept.
//-----------------------------------------------------------------------------
void Clipper3D::SaveClipResults( void )
{
// sanity check!
if( IsEmpty() )
return;
// mark this place in the history
GetHistory()->MarkUndoPosition( NULL, "Clip Objects" );
//
// save all new objects into the selection list
//
FOR_EACH_OBJ( m_ClipResults, pos )
{
CClipGroup *pClipGroup = m_ClipResults.Element( pos );
if( !pClipGroup )
continue;
CMapSolid *pOrigSolid = pClipGroup->GetOrigSolid();
CMapSolid *pBackSolid = pClipGroup->GetClipSolid( CClipGroup::BACK );
CMapSolid *pFrontSolid = pClipGroup->GetClipSolid( CClipGroup::FRONT );
//
// save the front clip solid and clear the clip results list of itself
//
if( pFrontSolid )
{
SaveClipSolid( pFrontSolid, pOrigSolid );
pClipGroup->SetClipSolid( NULL, CClipGroup::FRONT );
}
//
// save the front clip solid and clear the clip results list of itself
//
if( pBackSolid )
{
SaveClipSolid( pBackSolid, pOrigSolid );
pClipGroup->SetClipSolid( NULL, CClipGroup::BACK );
}
// Send the notification that this solid as been clipped.
pOrigSolid->PostUpdate( Notify_Clipped );
// remove the original solid
RemoveOrigSolid( pOrigSolid );
}
// set the the clipping results list as empty
ResetClipResults();
// update world and views
m_pDocument->SetModifiedFlag();
}
//-----------------------------------------------------------------------------
// Purpose: Draws the measurements of a brush in the 2D view.
// Input : pRender -
// pSolid -
// nFlags -
//-----------------------------------------------------------------------------
void Clipper3D::DrawBrushExtents( CRender2D *pRender, CMapSolid *pSolid, int nFlags )
{
//
// get the bounds of the solid
//
Vector Mins, Maxs;
pSolid->GetRender2DBox( Mins, Maxs );
//
// Determine which side of the clipping plane this solid is on in screen
// space. This tells us where to draw the extents.
//
if( ( m_ClipPlane.normal[0] == 0 ) && ( m_ClipPlane.normal[1] == 0 ) && ( m_ClipPlane.normal[2] == 0 ) )
return;
Vector normal = m_ClipPlane.normal;
if( nFlags & DBT_BACK )
{
VectorNegate( normal );
}
Vector2D planeNormal;
pRender->TransformNormal( planeNormal, normal );
if( planeNormal.x <= 0 )
{
nFlags &= ~DBT_RIGHT;
nFlags |= DBT_LEFT;
}
else if( planeNormal.x > 0 )
{
nFlags &= ~DBT_LEFT;
nFlags |= DBT_RIGHT;
}
if( planeNormal.y <= 0 )
{
nFlags &= ~DBT_BOTTOM;
nFlags |= DBT_TOP;
}
else if( planeNormal.y > 0 )
{
nFlags &= ~DBT_TOP;
nFlags |= DBT_BOTTOM;
}
DrawBoundsText(pRender, Mins, Maxs, nFlags);
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : *pRender -
//-----------------------------------------------------------------------------
void Clipper3D::RenderTool2D(CRender2D *pRender)
{
if ( IsEmpty() )
return;
// check flag for rendering vertices
bool bDrawVerts = ( bool )( Options.view2d.bDrawVertices == TRUE );
// setup the line to use
pRender->SetDrawColor( 255, 255, 255 );
//
// render the clipped solids
//
FOR_EACH_OBJ( m_ClipResults, pos )
{
CClipGroup *pClipGroup = m_ClipResults.Element( pos );
CMapSolid *pClipBack = pClipGroup->GetClipSolid( CClipGroup::BACK );
CMapSolid *pClipFront = pClipGroup->GetClipSolid( CClipGroup::FRONT );
if( !pClipBack && !pClipFront )
continue;
//
// draw clip solids with the extents
//
if( pClipBack )
{
int faceCount = pClipBack->GetFaceCount();
for( int i = 0; i < faceCount; i++ )
{
CMapFace *pFace = pClipBack->GetFace( i );
// size 4
pRender->DrawPolyLine( pFace->nPoints, pFace->Points );
if ( bDrawVerts )
{
pRender->DrawHandles( pFace->nPoints, pFace->Points );
}
if( m_bDrawMeasurements )
{
DrawBrushExtents( pRender, pClipBack, DBT_TOP | DBT_LEFT | DBT_BACK );
}
}
}
if( pClipFront )
{
int faceCount = pClipFront->GetFaceCount();
for( int i = 0; i < faceCount; i++ )
{
CMapFace *pFace = pClipFront->GetFace( i );
pRender->DrawPolyLine( pFace->nPoints, pFace->Points );
if ( bDrawVerts )
{
pRender->DrawHandles( pFace->nPoints, pFace->Points );
}
if( m_bDrawMeasurements )
{
DrawBrushExtents( pRender, pClipFront, DBT_BOTTOM | DBT_RIGHT );
}
}
}
}
//
// draw the clip-plane
//
pRender->SetDrawColor( 0, 255, 255 );
pRender->DrawLine( m_ClipPoints[0], m_ClipPoints[1] );
//
// draw the clip-plane endpoints
//
pRender->SetHandleStyle( HANDLE_RADIUS, CRender::HANDLE_SQUARE );
pRender->SetHandleColor( 255, 255, 255 );
pRender->DrawHandle( m_ClipPoints[0] );
pRender->DrawHandle( m_ClipPoints[1] );
}
//-----------------------------------------------------------------------------
// Purpose: Renders the brushes that will be left by the clipper in white
// wireframe.
// Input : pRender - Rendering interface.
//-----------------------------------------------------------------------------
void Clipper3D::RenderTool3D( CRender3D *pRender )
{
// is there anything to render?
if( m_bEmpty )
return;
//
// setup the renderer
//
pRender->PushRenderMode( RENDER_MODE_WIREFRAME );
FOR_EACH_OBJ( m_ClipResults, pos )
{
CClipGroup *pClipGroup = m_ClipResults.Element( pos );
CMapSolid *pFrontSolid = pClipGroup->GetClipSolid( CClipGroup::FRONT );
if( pFrontSolid )
{
color32 rgbColor = pFrontSolid->GetRenderColor();
pFrontSolid->SetRenderColor(255, 255, 255);
pFrontSolid->Render3D(pRender);
pFrontSolid->SetRenderColor(rgbColor);
}
CMapSolid *pBackSolid = pClipGroup->GetClipSolid( CClipGroup::BACK );
if( pBackSolid )
{
color32 rgbColor = pBackSolid->GetRenderColor();
pBackSolid->SetRenderColor(255, 255, 255);
pBackSolid->Render3D(pRender);
pBackSolid->SetRenderColor(rgbColor);
}
}
pRender->PopRenderMode();
}
//-----------------------------------------------------------------------------
// Purpose: (virtual imp)
// Input : pt -
// BOOL -
// Output : int
//-----------------------------------------------------------------------------
int Clipper3D::HitTest(CMapView *pView, const Vector2D &ptClient, bool bTestHandles)
{
// check points
for ( int i=0; i<2;i++ )
{
if ( HitRect(pView, ptClient, m_ClipPoints[i], HANDLE_RADIUS) )
{
return i+1; // return clip point index + 1
}
}
// neither point hit
return 0;
}
//-----------------------------------------------------------------------------
// Purpose: Reset (clear) the clip results.
//-----------------------------------------------------------------------------
void Clipper3D::ResetClipResults( void )
{
//
// delete the clip solids held in the list -- originals are just pointers
// to pre-existing objects
//
FOR_EACH_OBJ( m_ClipResults, pos )
{
CClipGroup *pClipGroup = m_ClipResults.Element(pos);
if( pClipGroup )
{
delete pClipGroup;
}
}
m_ClipResults.RemoveAll();
// the clipping list is empty
SetEmpty();
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : nChar -
// nRepCnt -
// nFlags -
//-----------------------------------------------------------------------------
bool Clipper3D::OnKeyDown2D(CMapView2D *pView, UINT nChar, UINT nRepCnt, UINT nFlags)
{
switch (nChar)
{
case 'O':
{
//
// Toggle the rendering of measurements.
//
ToggleMeasurements();
return true;
}
case VK_RETURN:
{
//
// Do the clip.
//
if (!IsEmpty() )
{
SaveClipResults();
}
return true;
}
case VK_ESCAPE:
{
OnEscape();
return true;
}
}
return false;
}
//-----------------------------------------------------------------------------
// 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 Clipper3D::OnLMouseDown2D(CMapView2D *pView, UINT nFlags, const Vector2D &vPoint)
{
Tool3D::OnLMouseDown2D(pView, nFlags, vPoint);
unsigned int uConstraints = GetConstraints( nFlags );
//
// Convert point to world coords.
//
Vector vecWorld;
pView->ClientToWorld(vecWorld, vPoint);
vecWorld[pView->axThird] = COORD_NOTINIT;
// getvisiblepoint fills in any coord that's still set to COORD_NOTINIT:
m_pDocument->GetBestVisiblePoint(vecWorld);
// snap starting position to grid
if ( uConstraints & constrainSnap )
m_pDocument->Snap(vecWorld, uConstraints);
bool bStarting = false;
// if the tool is not empty, and shift is not held down (to
// start a new camera), don't do anything.
if(!IsEmpty())
{
// test for clip point hit (result = {0, 1, 2}
int hitPoint = HitTest( pView, vPoint );
if ( hitPoint > 0 )
{
// test for clip point hit (result = {0, 1, -1})
m_ClipPointHit = hitPoint-1; // convert back to index
m_vOrgPos = m_ClipPoints[m_ClipPointHit];
StartTranslation( pView, vPoint );
}
else if ( m_vPlaneNormal != pView->GetViewAxis() )
{
SetEmpty();
bStarting = true;
}
else
{
if (nFlags & MK_SHIFT)
{
SetEmpty();
bStarting = true;
}
else
{
return true; // do nothing;
}
}
}
else
{
bStarting = true;
}
SetClipObjects(m_pDocument->GetSelection()->GetList());
if (bStarting)
{
// start the tools translation functionality
StartTranslation( pView, vPoint );
// set the initial clip points
m_ClipPointHit = 0;
m_ClipPoints[0] = vecWorld;
m_ClipPoints[1] = vecWorld;
m_vOrgPos = vecWorld;
}
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 Clipper3D::OnLMouseUp2D(CMapView2D *pView, UINT nFlags, const Vector2D &vPoint)
{
Tool3D::OnLMouseUp2D(pView, nFlags, vPoint);
if ( IsTranslating() )
{
FinishTranslation(true);
}
m_pDocument->UpdateStatusbar();
return true;
}
unsigned int Clipper3D::GetConstraints(unsigned int nKeyFlags)
{
unsigned int uConstraints = Tool3D::GetConstraints( nKeyFlags );
if(nKeyFlags & MK_CONTROL)
{
uConstraints |= constrainMoveAll;
}
return uConstraints;
}
//-----------------------------------------------------------------------------
// 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 Clipper3D::OnMouseMove2D(CMapView2D *pView, UINT nFlags, const Vector2D &vPoint)
{
vgui::HCursor hCursor = vgui::dc_arrow;
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())
{
// cursor is cross here
Tool3D::UpdateTranslation( pView, vPoint, uConstraints);
hCursor = vgui::dc_none;
}
else if (!IsEmpty())
{
//
// If the cursor is on a handle, set it to a cross.
//
if (HitTest( pView, vPoint, true))
{
hCursor = vgui::dc_crosshair;
}
}
if ( hCursor != vgui::dc_none )
pView->SetCursor( hCursor );
return true;
}
//-----------------------------------------------------------------------------
// Purpose: Handles character events.
// Input : Per CWnd::OnKeyDown.
// Output : Returns true if the message was handled, false if not.
//-----------------------------------------------------------------------------
bool Clipper3D::OnKeyDown3D(CMapView3D *pView, UINT nChar, UINT nRepCnt, UINT nFlags)
{
switch (nChar)
{
case VK_RETURN:
{
if (!IsEmpty()) // dvs: what does isempty mean for the clipper?
{
SaveClipResults();
}
return true;
}
case VK_ESCAPE:
{
OnEscape();
return true;
}
}
return false;
}
//-----------------------------------------------------------------------------
// Purpose: Handles the escape key in the 2D or 3D views.
//-----------------------------------------------------------------------------
void Clipper3D::OnEscape(void)
{
// If we're clipping, clear it
if (!IsEmpty())
{
SetEmpty();
}
else
{
m_pDocument->GetTools()->SetTool(TOOL_POINTER);
}
}

186
hammer/ToolClipper.h Normal file
View File

@@ -0,0 +1,186 @@
//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//=============================================================================//
#ifndef CLIPPER3D_H
#define CLIPPER3D_H
#ifdef _WIN32
#pragma once
#endif
#include "MapClass.h" // For CMapObjectList
#include "Tool3D.h"
#include "ToolInterface.h"
#include "Render2D.h"
#include "MapFace.h"
class CMapSolid;
//=============================================================================
//
// CClipGroup
//
class CClipGroup
{
public:
enum { FRONT = 0, BACK };
inline CClipGroup();
~CClipGroup();
inline void SetOrigSolid( CMapSolid *pSolid );
inline CMapSolid *GetOrigSolid( void );
inline void SetClipSolid( CMapSolid *pSolid, int side );
inline CMapSolid *GetClipSolid( int side );
private:
CMapSolid *m_pOrigSolid;
CMapSolid *m_pClipSolids[2]; // front, back
};
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
inline CClipGroup::CClipGroup()
{
m_pOrigSolid = NULL;
m_pClipSolids[0] = NULL;
m_pClipSolids[1] = NULL;
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
inline void CClipGroup::SetOrigSolid( CMapSolid *pSolid )
{
m_pOrigSolid = pSolid;
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
inline CMapSolid *CClipGroup::GetOrigSolid( void )
{
return m_pOrigSolid;
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
inline void CClipGroup::SetClipSolid( CMapSolid *pSolid, int side )
{
m_pClipSolids[side] = pSolid;
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
inline CMapSolid *CClipGroup::GetClipSolid( int side )
{
return m_pClipSolids[side];
}
class Clipper3D : public Tool3D
{
friend BOOL AddToClipList( CMapSolid *pSolid, Clipper3D *pClipper );
public:
enum { FRONT = 0, BACK, BOTH };
Clipper3D();
~Clipper3D();
void IterateClipMode( void );
inline void ToggleMeasurements( void );
//
// Tool3D implementation.
//
virtual int HitTest( CMapView *pView, const Vector2D &vPoint, bool bTestHandles = false );
virtual unsigned int GetConstraints(unsigned int nKeyFlags);
//
// CBaseTool implementation.
//
virtual void OnActivate();
virtual void OnDeactivate();
virtual ToolID_t GetToolID(void) { return TOOL_CLIPPER; }
virtual void RenderTool2D(CRender2D *pRender);
virtual void RenderTool3D(CRender3D *pRender);
virtual bool OnKeyDown2D(CMapView2D *pView, UINT nChar, UINT nRepCnt, UINT nFlags);
virtual bool OnKeyDown3D(CMapView3D *pView, UINT nChar, UINT nRepCnt, UINT nFlags);
virtual bool OnLMouseDown2D(CMapView2D *pView, UINT nFlags, const Vector2D &vPoint);
virtual bool OnLMouseUp2D(CMapView2D *pView, UINT nFlags, const Vector2D &vPoint);
virtual bool OnMouseMove2D(CMapView2D *pView, UINT nFlags, const Vector2D &vPoint);
protected:
//
// Tool3D implementation.
//
virtual bool UpdateTranslation( const Vector &vUpdate, UINT uFlags = 0 );
virtual void FinishTranslation( bool bSave );
private:
void OnEscape(void);
void SetClipObjects( const CMapObjectList *pList );
void SetClipPlane( PLANE *pPlane );
void BuildClipPlane( void );
void SaveClipResults( void );
void GetClipResults( void );
void CalcClipResults( void );
void ResetClipResults( void );
void RemoveOrigSolid( CMapSolid *pOrigSolid );
void SaveClipSolid( CMapSolid *pSolid, CMapSolid *pOrigSolid );
void DrawBrushExtents(CRender2D *pRender, CMapSolid *pSolid, int nFlags);
int m_Mode; // current clipping mode { back, front, both }
PLANE m_ClipPlane; // the clipping plane -- front/back is uneccesary
Vector m_ClipPoints[2]; // 2D clipping points -- used to create the clip plane
int m_ClipPointHit; // the clipping that was "hit" {0, 1, -1}
Vector m_vOrgPos;
const CMapObjectList *m_pOrigObjects; // list of the initial objects to clip
CUtlVector<CClipGroup*> m_ClipResults; // list of clipped objects
bool m_bDrawMeasurements; // Whether to draw brush dimensions in the 2D view.
CRender2D m_Render2D; // 2d renderer
};
//-----------------------------------------------------------------------------
// Purpose: Toggles the clipper's rendering of brush measurements in the 2D view.
//-----------------------------------------------------------------------------
inline void Clipper3D::ToggleMeasurements( void )
{
m_bDrawMeasurements = !m_bDrawMeasurements;
m_pDocument->UpdateAllViews( MAPVIEW_UPDATE_TOOL );
}
#endif // CLIPPER3D_H

400
hammer/ToolCordon.cpp Normal file
View File

@@ -0,0 +1,400 @@
//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============//
//
// Purpose: Implements the cordon tool. The cordon tool defines a rectangular
// volume that acts as a visibility filter. Only objects that intersect
// the cordon are rendered in the views. When saving the MAP file while
// the cordon tool is active, only brushes that intersect the cordon
// bounds are saved. The cordon box is replaced by brushes in order to
// seal the map.
//
//=============================================================================//
#include "stdafx.h"
#include "ChunkFile.h"
#include "ToolCordon.h"
#include "History.h"
#include "GlobalFunctions.h"
#include "MainFrm.h"
#include "MapDoc.h"
#include "MapDefs.h"
#include "MapSolid.h"
#include "MapView2D.h"
#include "MapView3D.h"
#include "MapWorld.h"
#include "render2d.h"
#include "StatusBarIDs.h"
#include "ToolManager.h"
#include "Options.h"
#include "WorldSize.h"
#include "vgui/Cursor.h"
// memdbgon must be the last include file in a .cpp file!!!
#include <tier0/memdbgon.h>
Vector Cordon3D::m_vecLastMins; // Last mins & maxs the user dragged out with this tool;
Vector Cordon3D::m_vecLastMaxs; // used to fill in the third axis when starting a new box.
//-----------------------------------------------------------------------------
// Purpose: Constructor.
//-----------------------------------------------------------------------------
Cordon3D::Cordon3D(void)
{
SetDrawColors(RGB(0, 255, 255), RGB(255, 0, 0));
m_vecLastMins.Init( COORD_NOTINIT, COORD_NOTINIT, COORD_NOTINIT );
m_vecLastMaxs.Init( COORD_NOTINIT, COORD_NOTINIT, COORD_NOTINIT );
}
//-----------------------------------------------------------------------------
// Purpose: Called when the tool is activated.
// Input : eOldTool - The ID of the previously active tool.
//-----------------------------------------------------------------------------
void Cordon3D::OnActivate()
{
RefreshToolState();
}
//-----------------------------------------------------------------------------
// Fetch data from the document and update our internal state.
//-----------------------------------------------------------------------------
void Cordon3D::RefreshToolState()
{
Vector mins( COORD_NOTINIT, COORD_NOTINIT, COORD_NOTINIT );
Vector maxs( COORD_NOTINIT, COORD_NOTINIT, COORD_NOTINIT );
if ( m_pDocument->Cordon_GetCount() > 0 )
{
m_pDocument->Cordon_GetEditCordon( mins, maxs );
m_vecLastMins = mins;
m_vecLastMaxs = maxs;
}
SetBounds( mins, maxs );
m_bEmpty = !IsValidBox();
EnableHandles( true );
}
//-----------------------------------------------------------------------------
// Return true of the boxes intersect (but not if they just touch).
//-----------------------------------------------------------------------------
inline bool BoxesIntersect2D( const Vector2D &vBox1Min, const Vector2D &vBox1Max, const Vector2D &vBox2Min, const Vector2D &vBox2Max )
{
return ( vBox1Min.x < vBox2Max.x ) && ( vBox1Max.x > vBox2Min.x ) &&
( vBox1Min.y < vBox2Max.y ) && ( vBox1Max.y > vBox2Min.y );
}
//-----------------------------------------------------------------------------
// 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 Cordon3D::OnLMouseDown2D(CMapView2D *pView, UINT nFlags, const Vector2D &vPoint)
{
Tool3D::OnLMouseDown2D(pView, nFlags, vPoint);
Vector vecWorld;
pView->ClientToWorld(vecWorld, vPoint);
unsigned int uConstraints = GetConstraints( nFlags );
if ( HitTest(pView, vPoint, true) )
{
StartTranslation( pView, vPoint, m_LastHitTestHandle );
}
else
{
//
// Test against all active cordons
//
CMapDoc *pDoc = pView->GetMapDoc();
if ( !pDoc )
return true;
// Make a little box around the cursor to test against.
const int iSelUnits = 2;
Vector2D selMins( vPoint.x - iSelUnits, vPoint.y - iSelUnits );
Vector2D selMaxs( vPoint.x + iSelUnits, vPoint.y + iSelUnits );
for ( int i = 0; i < pDoc->Cordon_GetCount(); i++ )
{
Cordon_t *cordon = pDoc->Cordon_GetCordon( i );
if ( !cordon->m_bActive )
continue;
for ( int j = 0; j < cordon->m_Boxes.Count(); j++ )
{
BoundBox *box = &cordon->m_Boxes[j];
Vector2D vecClientMins;
Vector2D vecClientMaxs;
pView->WorldToClient( vecClientMins, box->bmins );
pView->WorldToClient( vecClientMaxs, box->bmaxs );
// 2D projection can flip Y
NormalizeBox( vecClientMins, vecClientMaxs );
if ( BoxesIntersect2D( vecClientMins, vecClientMaxs, selMins, selMaxs ) )
{
pDoc->Cordon_SelectCordonForEditing( cordon, box, SELECT_CORDON_FROM_TOOL );
RefreshToolState();
return true;
}
}
}
// getvisiblepoint fills in any coord that's still set to COORD_NOTINIT:
vecWorld[pView->axThird] = m_vecLastMins[pView->axThird];
m_pDocument->GetBestVisiblePoint(vecWorld);
// snap starting position to grid
if ( uConstraints & constrainSnap )
m_pDocument->Snap(vecWorld,uConstraints);
// Create a new box
// Add it to the current edit cordon
// Set the edit cordon to current edit cordon and the new box
Cordon_t *cordon = m_pDocument->Cordon_GetSelectedCordonForEditing();
BoundBox *box = NULL;
if ( !cordon )
{
// No cordon, we need a new one.
cordon = m_pDocument->Cordon_CreateNewCordon( DEFAULT_CORDON_NAME, &box );
}
else
{
// Just add a box to the current cordon.
box = m_pDocument->Cordon_AddBox( cordon );
}
box->bmins = box->bmaxs = vecWorld;
Vector vecSize( 0, 0, 0 );
if ( ( m_vecLastMins[pView->axThird] == COORD_NOTINIT ) || ( m_vecLastMaxs[pView->axThird] == COORD_NOTINIT ) )
{
vecSize[pView->axThird] = pDoc->GetGridSpacing();
}
else
{
vecSize[pView->axThird] = m_vecLastMaxs[pView->axThird] - m_vecLastMins[pView->axThird];
}
StartNew( pView, vPoint, vecWorld, vecSize );
m_pDocument->Cordon_SelectCordonForEditing( cordon, box, SELECT_CORDON_FROM_TOOL );
if ( pDoc->Cordon_IsCordoning() )
{
pDoc->UpdateVisibilityAll();
}
pDoc->SetModifiedFlag( 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 Cordon3D::OnLMouseUp2D(CMapView2D *pView, UINT nFlags, const Vector2D &vPoint)
{
bool bShift = ( nFlags & MK_SHIFT ) != 0;
Tool3D::OnLMouseUp2D(pView, nFlags, vPoint) ;
if ( IsTranslating() )
{
if ( bShift )
{
}
FinishTranslation( true );
if ( bShift )
{
// Clone the selected cordon
Cordon_t *cordon = m_pDocument->Cordon_GetSelectedCordonForEditing();
BoundBox *box = m_pDocument->Cordon_AddBox( cordon );
box->bmins = bmins;
box->bmaxs = bmaxs;
m_pDocument->Cordon_SelectCordonForEditing( cordon, box, SELECT_CORDON_FROM_TOOL );
RefreshToolState();
}
else
{
m_pDocument->Cordon_SetEditCordon( bmins, bmaxs );
// Remember these bounds for the next time we start dragging out a new cordon.
m_vecLastMins = bmins;
m_vecLastMaxs = bmaxs;
}
}
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 Cordon3D::OnMouseMove2D(CMapView2D *pView, UINT nFlags, const Vector2D &vPoint)
{
vgui::HCursor hCursor = vgui::dc_arrow;
Tool3D::OnMouseMove2D(pView, nFlags, vPoint) ;
unsigned int uConstraints = GetConstraints( nFlags );
// Convert to world coords.
Vector vecWorld;
pView->ClientToWorld(vecWorld, vPoint);
// Update status bar position display.
//
char szBuf[128];
m_pDocument->Snap(vecWorld,uConstraints);
sprintf(szBuf, " @%.0f, %.0f ", vecWorld[pView->axHorz], vecWorld[pView->axVert]);
SetStatusText(SBI_COORDS, szBuf);
if ( IsTranslating() )
{
// cursor is cross here
Tool3D::UpdateTranslation( pView, vPoint, uConstraints );
hCursor = vgui::dc_none;
}
else if ( HitTest(pView, vPoint, true) )
{
hCursor = UpdateCursor( pView, m_LastHitTestHandle, m_TranslateMode );
}
if ( hCursor != vgui::dc_none )
pView->SetCursor( hCursor );
return true;
}
//-----------------------------------------------------------------------------
// Purpose: Handles the escape key in the 2D or 3D views.
//-----------------------------------------------------------------------------
void Cordon3D::OnEscape(void)
{
if ( IsTranslating() )
{
FinishTranslation( false );
}
else
{
m_pDocument->GetTools()->SetTool(TOOL_POINTER);
}
}
//-----------------------------------------------------------------------------
// Deletes the currently selected cordon in response to the user hitting DELETE
//-----------------------------------------------------------------------------
void Cordon3D::OnDelete()
{
BoundBox *pBox = NULL;
Cordon_t *pCordon = m_pDocument->Cordon_GetSelectedCordonForEditing( &pBox );
if ( !pCordon || !pBox )
return;
if ( !pBox || ( pCordon->m_Boxes.Count() <= 1 ) )
{
m_pDocument->Cordon_RemoveCordon( pCordon );
}
else
{
m_pDocument->Cordon_RemoveBox( pCordon, pBox );
}
m_pDocument->UpdateVisibilityAll();
m_pDocument->SetModifiedFlag( true );
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
bool Cordon3D::OnKeyDown2D(CMapView2D *pView, UINT nChar, UINT nRepCnt, UINT nFlags)
{
if (nChar == VK_ESCAPE)
{
OnEscape();
return true;
}
if ( nChar == VK_DELETE )
{
OnDelete();
}
return false;
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
bool Cordon3D::OnKeyDown3D(CMapView3D *pView, UINT nChar, UINT nRepCnt, UINT nFlags)
{
if (nChar == VK_ESCAPE)
{
OnEscape();
return true;
}
if ( nChar == VK_DELETE )
{
OnDelete();
}
return false;
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void Cordon3D::RenderTool2D( CRender2D *pRender )
{
pRender->PushRenderMode( RENDER_MODE_DOTTED );
pRender->SetDrawColor( 255, 255, 0 );
// If cordoning is active, the document's rendering code handles drawing the cordons.
if ( !m_pDocument->Cordon_IsCordoning() )
{
int nCount = m_pDocument->Cordon_GetCount();
for ( int i = 0; i < nCount; i++ )
{
Cordon_t *pCordon = m_pDocument->Cordon_GetCordon( i );
if ( pCordon->m_bActive )
{
for ( int j = 0; j < pCordon->m_Boxes.Count(); j++ )
{
// draw simple rectangle
pRender->DrawRectangle( pCordon->m_Boxes[j].bmins, pCordon->m_Boxes[j].bmaxs, false, 0 );
}
}
}
}
pRender->PopRenderMode();
BaseClass::RenderTool2D( pRender );
}

61
hammer/ToolCordon.h Normal file
View File

@@ -0,0 +1,61 @@
//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ====
//
// Defines the cordon tool. The cordon tool defines a rectangular
// volume that acts as a visibility filter. Only objects that intersect
// the cordon are rendered in the views. When saving the MAP file while
// the cordon tool is active, only brushes that intersect the cordon
// bounds are saved. The cordon box is replaced by brushes in order to
// seal the map.
//
//=============================================================================
#ifndef CORDON3D_H
#define CORDON3D_H
#pragma once
#include "Box3D.h"
#include "ToolInterface.h"
class CChunkFile;
class CSaveInfo;
class CMapWorld;
class CMapView2D;
class CMapView3D;
enum ChunkFileResult_t;
class Cordon3D : public Box3D
{
typedef Box3D BaseClass;
public:
Cordon3D(void);
// CBaseTool implementation.
virtual void OnActivate();
virtual ToolID_t GetToolID(void) { return TOOL_EDITCORDON; }
virtual void RefreshToolState();
virtual bool OnLMouseDown2D(CMapView2D *pView, UINT nFlags, const Vector2D &vPoint);
virtual bool OnLMouseUp2D(CMapView2D *pView, UINT nFlags, const Vector2D &vPoint);
virtual bool OnMouseMove2D(CMapView2D *pView, UINT nFlags, const Vector2D &vPoint);
virtual bool OnKeyDown2D(CMapView2D *pView, UINT nChar, UINT nRepCnt, UINT nFlags);
virtual bool OnKeyDown3D(CMapView3D *pView, UINT nChar, UINT nRepCnt, UINT nFlags);
virtual void RenderTool2D( CRender2D *pRender );
private:
void OnDelete();
void OnEscape();
static Vector m_vecLastMins; // Last mins & maxs the user dragged out with this tool;
static Vector m_vecLastMaxs; // used to fill in the third axis when starting a new box.
};
#endif // CORDON3D_H

163
hammer/ToolDecal.cpp Normal file
View File

@@ -0,0 +1,163 @@
//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//=============================================================================//
#include "stdafx.h"
#include "GlobalFunctions.h"
#include "History.h"
#include "MapDoc.h"
#include "MapDecal.h"
#include "MapSolid.h"
#include "MapView3D.h"
#include "resource.h"
#include "ToolManager.h"
#include "ToolDecal.h"
// memdbgon must be the last include file in a .cpp file!!!
#include <tier0/memdbgon.h>
//-----------------------------------------------------------------------------
// Purpose: Handles key down events in the 2D view.
// Input : Per CWnd::OnKeyDown.
// Output : Returns true on success, false on failure.
//-----------------------------------------------------------------------------
bool CToolDecal::OnKeyDown2D(CMapView2D *pView, UINT nChar, UINT nRepCnt, UINT nFlags)
{
switch (nChar)
{
case VK_ESCAPE:
{
ToolManager()->SetTool(TOOL_POINTER);
return true;
}
}
return false;
}
//-----------------------------------------------------------------------------
// 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 CToolDecal::OnMouseMove2D(CMapView2D *pView, UINT nFlags, const Vector2D &vPoint)
{
SetDecalCursor();
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 CToolDecal::OnKeyDown3D(CMapView3D *pView, UINT nChar, UINT nRepCnt, UINT nFlags)
{
switch (nChar)
{
case VK_ESCAPE:
{
ToolManager()->SetTool(TOOL_POINTER);
}
}
return false;
}
//-----------------------------------------------------------------------------
// Purpose: Handles left button down events in the 3D view.
// Input : Per CWnd::OnLButtonDown.
// Output : Returns true if the message was handled, false if not.
//-----------------------------------------------------------------------------
bool CToolDecal::OnLMouseDown3D(CMapView3D *pView, UINT nFlags, const Vector2D &vPoint)
{
//
// See if they clicked on a brush face. If so, apply a decal where they clicked.
//
CMapDoc *pDoc = pView->GetMapDoc();
ULONG ulFace;
CMapClass *pObject;
if ((pObject = pView->NearestObjectAt( vPoint, ulFace)) != NULL)
{
CMapSolid *pSolid = dynamic_cast <CMapSolid *> (pObject);
if (pSolid == NULL)
{
return true;
}
//
// Build a ray to trace against the face that they clicked on to
// find the point of intersection.
//
Vector Start,End;
pView->GetCamera()->BuildRay( vPoint, Start, End);
Vector HitPos, HitNormal;
CMapFace *pFace = pSolid->GetFace(ulFace);
if (pFace->TraceLine(HitPos, HitNormal, Start, End))
{
GetHistory()->MarkUndoPosition(NULL, "Create decal");
CMapEntity *pEntity = new CMapEntity;
pEntity->SetKeyValue("texture", GetDefaultTextureName());
pEntity->SetPlaceholder(TRUE);
pEntity->SetOrigin(HitPos);
pEntity->SetClass("infodecal");
CMapWorld *pWorld = pDoc->GetMapWorld();
CMapDecal *pDecal = pEntity->GetChildOfType((CMapDecal *)NULL);
if (pDecal != NULL)
{
pDecal->DecalAllSolids(pWorld);
}
pEntity->CalcBounds(TRUE);
pDoc->AddObjectToWorld(pEntity);
GetHistory()->KeepNew(pEntity);
pDoc->SetModifiedFlag();
}
}
return true;
}
//-----------------------------------------------------------------------------
// Purpose: Handles mouse move events in the 3D view.
// Input : Per CWnd::OnMouseMove.
// Output : Returns true if the message was handled, false if not.
//-----------------------------------------------------------------------------
bool CToolDecal::OnMouseMove3D(CMapView3D *pView, UINT nFlags, const Vector2D &vPoint)
{
SetDecalCursor();
return true;
}
//-----------------------------------------------------------------------------
// Purpose: Sets the cursor to the decal application cursor.
//-----------------------------------------------------------------------------
void CToolDecal::SetDecalCursor(void)
{
static HCURSOR hcurDecal;
if (!hcurDecal)
{
hcurDecal = LoadCursor(AfxGetInstanceHandle(), MAKEINTRESOURCE(IDC_DECAL));
}
SetCursor(hcurDecal);
}

39
hammer/ToolDecal.h Normal file
View File

@@ -0,0 +1,39 @@
//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//=============================================================================//
#ifndef TOOLDECAL_H
#define TOOLDECAL_H
#ifdef _WIN32
#pragma once
#endif
#include "ToolInterface.h"
class CToolDecal : public CBaseTool
{
public:
//
// CBaseTool implementation.
//
virtual ToolID_t GetToolID(void) { return TOOL_DECAL; }
virtual bool OnKeyDown2D(CMapView2D *pView, UINT nChar, UINT nRepCnt, UINT nFlags);
virtual bool OnMouseMove2D(CMapView2D *pView, UINT nFlags, const Vector2D &vPoint);
virtual bool OnKeyDown3D(CMapView3D *pView, UINT nChar, UINT nRepCnt, UINT nFlags);
virtual bool OnLMouseDown3D(CMapView3D *pView, UINT nFlags, const Vector2D &vPoint);
virtual bool OnMouseMove3D(CMapView3D *pView, UINT nFlags, const Vector2D &vPoint);
private:
void SetDecalCursor(void);
};
#endif // TOOLDECAL_H

730
hammer/ToolEntity.cpp Normal file
View File

@@ -0,0 +1,730 @@
//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============//
//
// Purpose: Implements the entity/prefab placement tool.
//
//=============================================================================//
#include "stdafx.h"
#include "History.h"
#include "MainFrm.h"
#include "MapDefs.h"
#include "MapSolid.h"
#include "MapDoc.h"
#include "MapView2D.h"
#include "MapView3D.h"
#include "Material.h"
#include "materialsystem/IMesh.h"
#include "Render2D.h"
#include "Render3D.h"
#include "StatusBarIDs.h"
#include "TextureSystem.h"
#include "ToolEntity.h"
#include "ToolManager.h"
#include "hammer.h"
#include "vgui/Cursor.h"
#include "Selection.h"
#include "vstdlib/random.h"
// memdbgon must be the last include file in a .cpp file!!!
#include <tier0/memdbgon.h>
//#pragma warning(disable:4244)
static HCURSOR s_hcurEntity = NULL;
class CToolEntityMessageWnd : public CWnd
{
public:
bool Create(void);
void PreMenu2D(CToolEntity *pTool, CMapView2D *pView);
protected:
//{{AFX_MSG_MAP(CToolEntityMessageWnd)
afx_msg void OnCreateObject();
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
private:
CToolEntity *m_pToolEntity;
CMapView2D *m_pView2D;
};
static CToolEntityMessageWnd s_wndToolMessage;
static const char *g_pszClassName = "ValveEditor_EntityToolWnd";
BEGIN_MESSAGE_MAP(CToolEntityMessageWnd, 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
// entity tool.
// Output : Returns true on success, false on failure.
//-----------------------------------------------------------------------------
bool CToolEntityMessageWnd::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 entity tool to this window before activating the context
// menu.
//-----------------------------------------------------------------------------
void CToolEntityMessageWnd::PreMenu2D(CToolEntity *pToolEntity, CMapView2D *pView)
{
Assert(pToolEntity != NULL);
m_pToolEntity = pToolEntity;
m_pView2D = pView;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CToolEntityMessageWnd::OnCreateObject()
{
m_pToolEntity->CreateMapObject(m_pView2D);
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
CToolEntity::CToolEntity(void)
{
SetEmpty();
m_vecPos.Init();
if (s_hcurEntity == NULL)
{
s_hcurEntity = LoadCursor(AfxGetInstanceHandle(), MAKEINTRESOURCE(IDC_ENTITY));
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
CToolEntity::~CToolEntity(void)
{
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : pt -
// BOOL -
// Output :
//-----------------------------------------------------------------------------
int CToolEntity::HitTest(CMapView *pView, const Vector2D &ptClient, bool bTestHandles)
{
return HitRect( pView, ptClient, m_vecPos, 8 )?TRUE:FALSE;
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : bSave -
//-----------------------------------------------------------------------------
void CToolEntity::FinishTranslation(bool bSave)
{
if (bSave)
{
TranslatePoint( m_vecPos );
m_bEmpty = false;
}
Tool3D::FinishTranslation(bSave);
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : pt -
// uFlags -
// size -
// Output : Returns true if the translation delta was nonzero.
//-----------------------------------------------------------------------------
bool CToolEntity::UpdateTranslation( const Vector &vUpdate, UINT uFlags)
{
Vector vOldDelta = m_vTranslation;
if ( !Tool3D::UpdateTranslation( vUpdate, uFlags ) )
return false;
// apply snap to grid constrain
if ( uFlags )
{
ProjectOnTranslationPlane( m_vecPos + m_vTranslation, m_vTranslation, uFlags );
m_vTranslation -= m_vecPos;
}
return true;
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : pRender -
//-----------------------------------------------------------------------------
void CToolEntity::RenderTool2D(CRender2D *pRender)
{
Vector v = m_vecPos;
if ( IsTranslating() )
{
TranslatePoint( v );
}
else if ( IsEmpty() )
{
return;
}
pRender->SetDrawColor( 35, 255, 75 );
//
// Draw center rect.
//
pRender->DrawRectangle( v, v, false, 6.0f );
//
// Draw crosshair
//
pRender->DrawLine( Vector( g_MIN_MAP_COORD, v.y, v.z), Vector( g_MAX_MAP_COORD, v.y , v.z) );
pRender->DrawLine( Vector( v.x, g_MIN_MAP_COORD, v.z), Vector( v.x, g_MAX_MAP_COORD, v.z) );
pRender->DrawLine( Vector( v.x, v.y, g_MIN_MAP_COORD), Vector( v.x, v.y, g_MAX_MAP_COORD) );
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : pView -
// point -
// Output :
//-----------------------------------------------------------------------------
bool CToolEntity::OnContextMenu2D(CMapView2D *pView, UINT nFlags, const Vector2D &vPoint)
{
if (!IsEmpty())
{
CMapDoc *pDoc = pView->GetMapDoc();
if (pDoc == NULL)
{
return true;
}
if (!pView->PointInClientRect(vPoint))
{
return true;
}
if ( HitTest( pView, vPoint, false) )
{
static CMenu menu, menuCreate;
static bool bInit = false;
if (!bInit)
{
bInit = true;
menu.LoadMenu(IDR_POPUPS);
menuCreate.Attach(::GetSubMenu(menu.m_hMenu, 1));
// Create the window that handles menu messages.
s_wndToolMessage.Create();
}
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:
// Input : *pView -
// nChar -
// nRepCnt -
// nFlags -
// Output : Returns true on success, false on failure.
//-----------------------------------------------------------------------------
bool CToolEntity::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:
// Input : pView -
// nFlags -
// point -
// Output : Returns true on success, false on failure.
//-----------------------------------------------------------------------------
bool CToolEntity::OnLMouseDown2D(CMapView2D *pView, UINT nFlags, const Vector2D &vPoint)
{
unsigned int uConstraints = GetConstraints( nFlags );
Tool3D::OnLMouseDown2D(pView, nFlags, vPoint);
if ( HitTest( pView, vPoint, false) )
{
// translate existing object
StartTranslation( pView, vPoint );
}
else
{
Vector vecWorld;
pView->ClientToWorld(vecWorld, vPoint );
//
// Snap starting position to grid.
//
if ( uConstraints & constrainSnap )
m_pDocument->Snap(vecWorld, uConstraints);
// create new one, keep old third axis
m_vecPos[pView->axHorz] = vecWorld[pView->axHorz];
m_vecPos[pView->axVert] = vecWorld[pView->axVert];
m_bEmpty = false;
StartTranslation( pView, vPoint );
}
return true;
}
// set temp transformation plane
void CToolEntity::StartTranslation( CMapView *pView, const Vector2D &vPoint )
{
Vector vOrigin, v1,v2,v3;
pView->GetBestTransformPlane( v1,v2,v3 );
SetTransformationPlane(m_vecPos, v1, v2, v3 );
// align translation plane to world origin
ProjectOnTranslationPlane( vec3_origin, vOrigin, 0 );
// set transformation plane
SetTransformationPlane(vOrigin, v1, v2, v3 );
Tool3D::StartTranslation( pView, vPoint, false );
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : Pre CWnd::OnLButtonUp.
// Output : Returns true on success, false on failure.
//-----------------------------------------------------------------------------
bool CToolEntity::OnLMouseUp2D(CMapView2D *pView, UINT nFlags, const Vector2D &vPoint)
{
Tool3D::OnLMouseUp2D(pView, nFlags, vPoint);
if (IsTranslating())
{
FinishTranslation( true );
}
m_pDocument->UpdateStatusbar();
return true;
}
//-----------------------------------------------------------------------------
// Returns true if the message was handled, false otherwise.
//-----------------------------------------------------------------------------
bool CToolEntity::OnMouseMove2D(CMapView2D *pView, UINT nFlags, const Vector2D &vPoint)
{
Tool3D::OnMouseMove2D(pView, nFlags, vPoint);
vgui::HCursor hCursor = vgui::dc_arrow;
unsigned int uConstraints = GetConstraints( nFlags );
// 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 we are currently dragging the marker, update that operation based on
// the current cursor position and keyboard state.
//
if (IsTranslating())
{
Tool3D::UpdateTranslation( pView, vPoint, uConstraints );
// Don't change the cursor while dragging - it should remain a cross.
hCursor = vgui::dc_none;
}
else if (!IsEmpty())
{
// Don't change the cursor while dragging - it should remain a cross.
hCursor = vgui::dc_crosshair;
}
if ( hCursor != vgui::dc_none )
pView->SetCursor( hCursor );
return true;
}
//-----------------------------------------------------------------------------
// Returns true if the message was handled, false otherwise.
//-----------------------------------------------------------------------------
bool CToolEntity::OnMouseMove3D(CMapView3D *pView, UINT nFlags, const Vector2D &vPoint)
{
return true;
}
//-----------------------------------------------------------------------------
// Returns true if the message was handled, false otherwise.
//-----------------------------------------------------------------------------
bool CToolEntity::OnKeyDown3D(CMapView3D *pView, UINT nChar, UINT nRepCnt, UINT nFlags)
{
CMapDoc *pDoc = pView->GetMapDoc();
if (pDoc == NULL)
{
return false;
}
switch (nChar)
{
case VK_RETURN:
{
//
// Create the entity or prefab.
//
if (!IsEmpty())
{
//CreateMapObject(pView); // TODO: support in 3D
}
return true;
}
case VK_ESCAPE:
{
OnEscape();
return true;
}
}
return false;
}
//-----------------------------------------------------------------------------
// Purpose: Handles the escape key in the 2D or 3D views.
//-----------------------------------------------------------------------------
void CToolEntity::OnEscape(void)
{
//
// Cancel the object creation tool.
//
if (!IsEmpty())
{
SetEmpty();
}
else
{
ToolManager()->SetTool(TOOL_POINTER);
}
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : *pView -
// nFlags -
// point -
// Output : Returns true on success, false on failure.
//-----------------------------------------------------------------------------
bool CToolEntity::OnLMouseDown3D(CMapView3D *pView, UINT nFlags, const Vector2D &vPoint)
{
ULONG ulFace;
VMatrix LocalMatrix, LocalMatrixNeg;
CMapClass *pObject = pView->NearestObjectAt( vPoint, ulFace, FLAG_OBJECTS_AT_RESOLVE_INSTANCES, &LocalMatrix );
Tool3D::OnLMouseDown3D(pView, nFlags, vPoint);
if (pObject != NULL)
{
CMapSolid *pSolid = dynamic_cast <CMapSolid *> (pObject);
if (pSolid == NULL)
{
// Clicked on a point entity - do nothing.
return true;
}
LocalMatrix.InverseTR( LocalMatrixNeg );
// Build a ray to trace against the face that they clicked on to
// find the point of intersection.
Vector Start,End;
pView->GetCamera()->BuildRay( vPoint, Start, End);
Vector HitPos, HitNormal;
CMapFace *pFace = pSolid->GetFace(ulFace);
Vector vFinalStart, vFinalEnd;
LocalMatrixNeg.V3Mul( Start, vFinalStart );
LocalMatrixNeg.V3Mul( End, vFinalEnd );
if (pFace->TraceLine( HitPos, HitNormal, vFinalStart, vFinalEnd))
{
Vector vFinalHitPos, vFinalHitNormal;
LocalMatrix.V3Mul( HitPos, vFinalHitPos );
vFinalHitNormal = LocalMatrix.ApplyRotation( HitNormal );
CMapClass *pNewObject = NULL;
if (GetMainWnd()->m_ObjectBar.IsEntityToolCreatingPrefab())
{
//
// Prefab creation.
//
unsigned int uConstraints = GetConstraints( nFlags );
m_pDocument->Snap(vFinalHitPos,uConstraints);
GetHistory()->MarkUndoPosition(m_pDocument->GetSelection()->GetList(), "New Prefab");
// Get prefab object
CMapClass *pPrefabObject = GetMainWnd()->m_ObjectBar.BuildPrefabObjectAtPoint(vFinalHitPos);
//
// Add prefab to the world.
//
CMapWorld *pWorld = m_pDocument->GetMapWorld();
m_pDocument->ExpandObjectKeywords(pPrefabObject, pWorld);
pNewObject = pPrefabObject;
}
else if (GetMainWnd()->m_ObjectBar.IsEntityToolCreatingEntity())
{
//
// Entity creation.
//
GetHistory()->MarkUndoPosition(m_pDocument->GetSelection()->GetList(), "New Entity");
CMapEntity *pEntity = new CMapEntity;
pEntity->SetPlaceholder(TRUE);
pEntity->SetOrigin(vFinalHitPos);
pEntity->SetClass(CObjectBar::GetDefaultEntityClass());
VPlane BeforeTransform( pFace->plane.normal, pFace->plane.dist ), AfterTransform;
LocalMatrix.TransformPlane( BeforeTransform, AfterTransform );
PLANE NewPlane;
NewPlane.dist = AfterTransform.m_Dist;
NewPlane.normal = AfterTransform.m_Normal;
// Align the entity on the plane properly
pEntity->AlignOnPlane(vFinalHitPos, &NewPlane, (vFinalHitNormal.z > 0.0f) ? CMapEntity::ALIGN_BOTTOM : CMapEntity::ALIGN_TOP);
pNewObject = pEntity;
}
if ( pNewObject )
{
if ( GetMainWnd()->m_ObjectBar.UseRandomYawOnEntityPlacement() )
{
// They checked "random yaw" on the object bar, so come up with a random yaw.
VMatrix vmRotate, vmT1, vmT2;
Vector vOrigin;
QAngle angRandom( 0, RandomInt( -180, 180 ), 0 );
pNewObject->GetOrigin( vOrigin );
// Setup a matrix that translates them to the origin, rotates it, then translates back.
MatrixFromAngles( angRandom, vmRotate );
MatrixBuildTranslation( vmT1, -vOrigin );
MatrixBuildTranslation( vmT2, vOrigin );
// Transform the object.
pNewObject->Transform( vmT2 * vmRotate * vmT1 );
}
m_pDocument->AddObjectToWorld( pNewObject );
GetHistory()->KeepNew( pNewObject );
// Select the new object.
m_pDocument->SelectObject( pNewObject, scClear|scSelect|scSaveChanges );
m_pDocument->SetModifiedFlag();
}
}
}
return true;
}
//-----------------------------------------------------------------------------
// Purpose: Renders a selection gizmo at our bounds center.
// Input : pRender - Rendering interface.
//-----------------------------------------------------------------------------
void CToolEntity::RenderTool3D(CRender3D *pRender)
{
Vector pos = m_vecPos;
if ( IsTranslating() )
{
TranslatePoint( pos );
}
else if ( IsEmpty() )
{
return;
}
//
// Setup the renderer.
//
pRender->PushRenderMode( RENDER_MODE_WIREFRAME);
CMeshBuilder meshBuilder;
CMatRenderContextPtr pRenderContext( MaterialSystemInterface() );
IMesh* pMesh = pRenderContext->GetDynamicMesh();
meshBuilder.Begin(pMesh, MATERIAL_LINES, 3);
meshBuilder.Position3f(g_MIN_MAP_COORD, pos.y, pos.z);
meshBuilder.Color3ub(255, 0, 0);
meshBuilder.AdvanceVertex();
meshBuilder.Position3f(g_MAX_MAP_COORD, pos.y, pos.z);
meshBuilder.Color3ub(255, 0, 0);
meshBuilder.AdvanceVertex();
meshBuilder.Position3f(pos.x, g_MIN_MAP_COORD, pos.z);
meshBuilder.Color3ub(0, 255, 0);
meshBuilder.AdvanceVertex();
meshBuilder.Position3f(pos.x, g_MAX_MAP_COORD, pos.z);
meshBuilder.Color3ub(0, 255, 0);
meshBuilder.AdvanceVertex();
meshBuilder.Position3f(pos.x, pos.y, g_MIN_MAP_COORD);
meshBuilder.Color3ub(0, 0, 255);
meshBuilder.AdvanceVertex();
meshBuilder.Position3f(pos.x, pos.y, g_MAX_MAP_COORD);
meshBuilder.Color3ub(0, 0, 255);
meshBuilder.AdvanceVertex();
meshBuilder.End();
pMesh->Draw();
pRender->PopRenderMode();
}
void CToolEntity::CreateMapObject(CMapView2D *pView)
{
CMapWorld *pWorld = m_pDocument->GetMapWorld();
CMapClass *pobj = NULL;
//
// Handle prefab creation.
//
if (GetMainWnd()->m_ObjectBar.IsEntityToolCreatingPrefab())
{
GetHistory()->MarkUndoPosition(m_pDocument->GetSelection()->GetList(), "New Prefab");
CMapClass *pPrefabObject = GetMainWnd()->m_ObjectBar.BuildPrefabObjectAtPoint(m_vecPos);
if (pPrefabObject == NULL)
{
pView->MessageBox("Unable to load prefab", "Error", MB_OK);
SetEmpty();
return;
}
m_pDocument->ExpandObjectKeywords(pPrefabObject, pWorld);
m_pDocument->AddObjectToWorld(pPrefabObject);
GetHistory()->KeepNew(pPrefabObject);
pobj = pPrefabObject;
}
//
// Handle entity creation.
//
else if (GetMainWnd()->m_ObjectBar.IsEntityToolCreatingEntity())
{
GetHistory()->MarkUndoPosition(m_pDocument->GetSelection()->GetList(), "New Entity");
CMapEntity *pEntity = new CMapEntity;
pEntity->SetPlaceholder(TRUE);
pEntity->SetOrigin(m_vecPos);
pEntity->SetClass(CObjectBar::GetDefaultEntityClass());
m_pDocument->AddObjectToWorld(pEntity);
pobj = pEntity;
GetHistory()->KeepNew(pEntity);
}
//
// Select the new object.
//
m_pDocument->SelectObject(pobj, scClear |scSelect|scSaveChanges);
SetEmpty();
m_pDocument->SetModifiedFlag();
}

79
hammer/ToolEntity.h Normal file
View File

@@ -0,0 +1,79 @@
//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ====
//
// Purpose: Defines the interface to the entity placement tool.
//
//=============================================================================
#ifndef TOOLENTITY_H
#define TOOLENTITY_H
#pragma once
#include "ToolInterface.h"
#include "Tool3D.h"
class CRender2D;
class CRender3D;
class CToolEntity : public Tool3D
{
friend class CToolEntityMessageWnd;
public:
CToolEntity(void);
~CToolEntity(void);
inline void GetPos(Vector &vecPos);
//
// CBaseTool implementation.
//
virtual ToolID_t GetToolID(void) { return TOOL_ENTITY; }
virtual bool OnContextMenu2D(CMapView2D *pView, UINT nFlags, const Vector2D &vPoint);
virtual bool OnKeyDown2D(CMapView2D *pView, UINT nChar, UINT nRepCnt, UINT nFlags);
virtual bool OnLMouseDown2D(CMapView2D *pView, UINT nFlags, const Vector2D &vPoint);
virtual bool OnLMouseUp2D(CMapView2D *pView, UINT nFlags, const Vector2D &vPoint);
virtual bool OnMouseMove2D(CMapView2D *pView, UINT nFlags, const Vector2D &vPoint);
virtual bool OnMouseMove3D(CMapView3D *pView, UINT nFlags, const Vector2D &vPoint);
virtual bool OnKeyDown3D(CMapView3D *pView, UINT nChar, UINT nRepCnt, UINT nFlags);
virtual bool OnLMouseDown3D(CMapView3D *pView, UINT nFlags, const Vector2D &vPoint);
virtual void RenderTool2D(CRender2D *pRender);
virtual void RenderTool3D(CRender3D *pRender);
protected:
//
// Tool3D implementation.
//
void StartTranslation( CMapView *pView, const Vector2D &vPoint);
virtual bool UpdateTranslation(const Vector &vUpdate, UINT flags);
virtual void FinishTranslation(bool bSave);
virtual int HitTest(CMapView *pView, const Vector2D &vPoint, bool bTestHandles = false);
private:
void OnEscape(void);
void CreateMapObject(CMapView2D *pView);
Vector m_vecPos; // Current position of the marker.
};
//-----------------------------------------------------------------------------
// Purpose: Returns the current position of the marker.
//-----------------------------------------------------------------------------
inline void CToolEntity::GetPos(Vector &vecPos)
{
vecPos = m_vecPos;
}
#endif // TOOLENTITY_H

85
hammer/ToolMagnify.cpp Normal file
View File

@@ -0,0 +1,85 @@
//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//=============================================================================//
#include "stdafx.h"
#include "MapDoc.h"
#include "MapView2D.h"
#include "resource.h"
#include "ToolMagnify.h"
#include "HammerVGui.h"
#include <VGuiMatSurface/IMatSystemSurface.h>
// memdbgon must be the last include file in a .cpp file!!!
#include <tier0/memdbgon.h>
//-----------------------------------------------------------------------------
// Purpose: Loads the cursor (only once).
//-----------------------------------------------------------------------------
CToolMagnify::CToolMagnify(void)
{
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : *pView -
// nFlags -
// point -
// Output : Returns true on success, false on failure.
//-----------------------------------------------------------------------------
bool CToolMagnify::OnContextMenu2D(CMapView2D *pView, UINT nFlags, const Vector2D &vPoint)
{
// Return true to suppress the default view context menu behavior.
return true;
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : pView -
// nFlags -
// point -
// Output : Returns true to indicate that the message was handled.
//-----------------------------------------------------------------------------
bool CToolMagnify::OnLMouseDown2D(CMapView2D *pView, UINT nFlags, const Vector2D &vPoint)
{
pView->SetZoom(pView->GetZoom() * 2);
pView->Invalidate();
return true;
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : pView -
// nFlags -
// point -
// Output : Returns true to indicate that the message was handled.
//-----------------------------------------------------------------------------
bool CToolMagnify::OnMouseMove2D(CMapView2D *pView, UINT nFlags, const Vector2D &vPoint)
{
// cursors are cached by surface
pView->SetCursor( "Resource/magnify.cur" );
return true;
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : pView -
// nFlags -
// point -
// Output : Returns true to indicate that the message was handled.
//-----------------------------------------------------------------------------
bool CToolMagnify::OnRMouseDown2D(CMapView2D *pView, UINT nFlags, const Vector2D &vPoint)
{
pView->SetZoom(pView->GetZoom() * 0.5f);
return true;
}

35
hammer/ToolMagnify.h Normal file
View File

@@ -0,0 +1,35 @@
//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//=============================================================================//
#ifndef TOOLMAGNIFY_H
#define TOOLMAGNIFY_H
#ifdef _WIN32
#pragma once
#endif
#include "ToolInterface.h"
class CToolMagnify : public CBaseTool
{
public:
CToolMagnify(void);
~CToolMagnify(void) {}
//
// CBaseTool implementation.
//
virtual ToolID_t GetToolID(void) { return TOOL_MAGNIFY; }
virtual bool OnContextMenu2D(CMapView2D *pView, UINT nFlags, const Vector2D &vPoint);
virtual bool OnLMouseDown2D(CMapView2D *pView, UINT nFlags, const Vector2D &vPoint);
virtual bool OnRMouseDown2D(CMapView2D *pView, UINT nFlags, const Vector2D &vPoint);
virtual bool OnMouseMove2D(CMapView2D *pView, UINT nFlags, const Vector2D &vPoint);
};
#endif // TOOLMAGNIFY_H

306
hammer/ToolManager.cpp Normal file
View File

@@ -0,0 +1,306 @@
//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// TODO: add an autoregistration system for tools a la LINK_ENTITY_TO_CLASS
//=============================================================================//
#include "stdafx.h"
#include "MapDoc.h"
#include "MainFrm.h"
#include "MapView2D.h" // FIXME: for MapView2D::updTool
#include "ToolAxisHandle.h"
#include "ToolDecal.h"
#include "ToolDisplace.h"
#include "ToolManager.h"
#include "ToolMagnify.h"
#include "ToolMaterial.h"
#include "ToolPickFace.h"
#include "ToolPickAngles.h"
#include "ToolPickEntity.h"
#include "ToolPointHandle.h"
#include "ToolSphere.h"
#include "ToolSweptHull.h"
#include "ToolBlock.h"
#include "ToolCamera.h"
#include "ToolClipper.h"
#include "ToolCordon.h"
#include "ToolEntity.h"
#include "ToolMorph.h"
#include "ToolOverlay.h"
#include "ToolSelection.h"
#include "ToolMagnify.h"
#include "ToolMaterial.h"
#include "toolsprinkle.h"
#include "ChunkFile.h"
// memdbgon must be the last include file in a .cpp file!!!
#include <tier0/memdbgon.h>
static CToolManager s_DummyToolmanager;
CToolManager* ToolManager()
{
CMapDoc *pDoc = CMapDoc::GetActiveMapDoc();
if ( pDoc )
return pDoc->GetTools();
return &s_DummyToolmanager;
}
//-----------------------------------------------------------------------------
// Purpose: Prepares the tool manager for use.
// Output : Returns true on success, false on failure.
//-----------------------------------------------------------------------------
bool CToolManager::Init( CMapDoc *pDocument )
{
// add default tools
//
// Create the tools that are held by the tool manager and add them
// to the internal tools list.
//
RemoveAllTools();
m_pDocument = pDocument;
AddTool( new CToolDisplace );
AddTool( new CToolMagnify );
AddTool( new CToolDecal );
AddTool( new CToolMaterial );
AddTool( new CToolAxisHandle );
AddTool( new CToolPointHandle );
AddTool( new CToolSphere );
AddTool( new CToolPickAngles );
AddTool( new CToolPickEntity );
AddTool( new CToolPickFace );
AddTool( new CToolSweptPlayerHull );
AddTool( new Selection3D );
AddTool( new CToolBlock );
AddTool( new CToolEntity );
AddTool( new Camera3D );
AddTool( new Morph3D );
AddTool( new Clipper3D );
AddTool( new Cordon3D );
AddTool( new CToolOverlay );
AddTool( new CToolEntitySprinkle );
return true;
}
//-----------------------------------------------------------------------------
// Purpose: Shuts down the tool manager - called on app exit.
//-----------------------------------------------------------------------------
void CToolManager::Shutdown()
{
m_pActiveTool = NULL;
m_pDocument = NULL;
RemoveAllTools();
}
//-----------------------------------------------------------------------------
// Purpose: Constructor. Allocates the tools.
//-----------------------------------------------------------------------------
CToolManager::CToolManager()
{
m_pActiveTool = NULL;
m_pDocument = NULL;
}
//-----------------------------------------------------------------------------
// Purpose: Destructor. Deletes the tools.
//-----------------------------------------------------------------------------
CToolManager::~CToolManager()
{
Shutdown();
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CToolManager::AddTool(CBaseTool *pTool)
{
if ( GetToolForID( pTool->GetToolID() ) )
{
Assert( !pTool );
Msg("CToolManager::AddTool: Tool %i already registered.\n");
return;
}
pTool->Init( m_pDocument );
m_Tools.AddToTail(pTool);
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
CBaseTool *CToolManager::GetActiveTool()
{
return m_pActiveTool;
}
//-----------------------------------------------------------------------------
// Purpose: Returns a tool pointer for a given tool ID, NULL if there is no
// corresponding tool.
//-----------------------------------------------------------------------------
CBaseTool *CToolManager::GetToolForID(ToolID_t eToolID)
{
int nToolCount = GetToolCount();
for (int i = 0; i < nToolCount; i++)
{
CBaseTool *pTool = GetTool(i);
if (pTool->GetToolID() == eToolID)
{
return pTool;
}
}
return NULL;
}
//-----------------------------------------------------------------------------
// Purpose: Returns the ID of the active tool.
//-----------------------------------------------------------------------------
ToolID_t CToolManager::GetActiveToolID()
{
if ( m_pActiveTool )
return m_pActiveTool->GetToolID();
else
return TOOL_NONE;
}
//-----------------------------------------------------------------------------
// Purpose: Pushes a new tool onto the tool stack and activates it. The active
// tool will be deactivated and reactivated when PopTool is called.
//-----------------------------------------------------------------------------
void CToolManager::PushTool(ToolID_t eToolID)
{
//
// Add the new tool to the top of the tool stack.
//
if (eToolID != GetActiveToolID())
{
m_ToolIDStack.AddToHead(GetActiveToolID());
}
SetTool(eToolID);
}
//-----------------------------------------------------------------------------
// Purpose: Restores the active tool to what it was when PushTool was called.
// If the stack is underflowed somehow, the pointer is restored.
//-----------------------------------------------------------------------------
void CToolManager::PopTool()
{
int nCount = m_ToolIDStack.Count();
if (nCount > 0)
{
ToolID_t eNewTool = m_ToolIDStack.Element(0);
m_ToolIDStack.Remove(0);
SetTool(eNewTool);
}
}
//-----------------------------------------------------------------------------
// Purpose: Sets the current active tool by ID.
// Input : iTool - ID of the tool to activate.
//-----------------------------------------------------------------------------
void CToolManager::SetTool(ToolID_t eNewTool)
{
CBaseTool *pNewTool = GetToolForID(eNewTool);
CBaseTool *pOldTool = m_pActiveTool;
// Check to see that we can deactive the current tool
if ( pOldTool && (pOldTool != pNewTool) )
{
// Deactivate the current tool unless we are just 'reactivating' it.
if( !pOldTool->CanDeactivate() )
return;
}
// set active tool to new tool already so old tool can peek whats coming next
m_pActiveTool = pNewTool;
// deactivate the old tool if different.
if ( pOldTool && (pOldTool != pNewTool) )
{
pOldTool->Deactivate();
}
// always activate the new tool
if ( pNewTool )
{
pNewTool->Activate();
}
// FIXME: When we start up, we end up here before the main window is created because
// CFaceEditDispPage::OnSetActive() calls SetTool(TOOL_FACEEDIT_DISP). This
// behavior is rather nonsensical during startup.
CMainFrame *pwndMain = GetMainWnd();
if (pwndMain != NULL)
{
pwndMain->m_ObjectBar.UpdateListForTool(eNewTool);
}
if ( m_pDocument )
m_pDocument->UpdateAllViews( MAPVIEW_UPDATE_TOOL );
}
ChunkFileResult_t CToolManager::SaveVMF(CChunkFile *pFile, CSaveInfo *pSaveInfo)
{
for (int i=0;i<m_Tools.Count(); i++)
{
if ( m_Tools[i]->GetVMFChunkName() != NULL )
{
m_Tools[i]->SaveVMF( pFile, pSaveInfo );
}
}
return ChunkFile_Ok;
}
ChunkFileResult_t CToolManager::LoadCallback(CChunkFile *pFile, CBaseTool *pTool)
{
return pTool->LoadVMF( pFile );
}
void CToolManager::AddToolHandlers( CChunkHandlerMap *pHandlersMap )
{
for (int i=0;i<m_Tools.Count(); i++)
{
if ( m_Tools[i]->GetVMFChunkName() != NULL )
{
pHandlersMap->AddHandler( m_Tools[i]->GetVMFChunkName(), (ChunkHandler_t)LoadCallback, m_Tools[i] );
}
}
}
//-----------------------------------------------------------------------------
// Purpose: Removes all the document-created tools from the tools list.
//-----------------------------------------------------------------------------
void CToolManager::RemoveAllTools()
{
m_pActiveTool = NULL;
m_Tools.PurgeAndDeleteElements();
m_ToolIDStack.RemoveAll();
}

96
hammer/ToolManager.h Normal file
View File

@@ -0,0 +1,96 @@
//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
//=============================================================================//
#ifndef TOOLMANAGER_H
#define TOOLMANAGER_H
#ifdef _WIN32
#pragma once
#endif
#include "ToolInterface.h"
#include "UtlVector.h"
class CToolAxisHandle;
class CToolDecal;
class CToolDisplace;
class CToolMagnify;
class CToolMaterial;
class CToolPickAngles;
class CToolPickEntity;
class CToolPickFace;
class CToolPointHandle;
class CToolSphere;
class CBaseTool;
class CToolSweptPlayerHull;
class CChunkHandlerMap;
class CToolManager
{
public:
CToolManager();
~CToolManager();
bool Init(CMapDoc *pDocument);
void Shutdown();
CBaseTool *GetActiveTool();
ToolID_t GetActiveToolID();
CBaseTool *GetToolForID(ToolID_t eToolID);
void SetTool(ToolID_t nToolID); // changes current tool without touching the tool stack
void PushTool(ToolID_t nToolID); // activates a new tool and put current tool on stack
void PopTool(); // restores last tool on stack
inline int GetToolCount();
inline CBaseTool *GetTool(int nIndex);
void RemoveAllTools();
void AddTool(CBaseTool *pTool);
static ChunkFileResult_t LoadCallback(CChunkFile *pFile, CBaseTool *pTool);
void AddToolHandlers( CChunkHandlerMap *pHandlersMap );
ChunkFileResult_t SaveVMF(CChunkFile *pFile, CSaveInfo *pSaveInfo);
ChunkFileResult_t LoadVMF(CChunkFile *pFile);
private:
void ActivateTool( CBaseTool *pTool );
void DeactivateTool( CBaseTool *pTool );
CUtlVector<CBaseTool *> m_Tools; // List of ALL the tools.
CMapDoc *m_pDocument; // document the manager is responisble for
CBaseTool *m_pActiveTool; // Pointer to the active new tool, NULL if none.
CUtlVector<ToolID_t> m_ToolIDStack; // Stack of active tool IDs, for PushTool/PopTool.
};
//-----------------------------------------------------------------------------
// Purpose: Accessor for iterating tools.
//-----------------------------------------------------------------------------
int CToolManager::GetToolCount()
{
return m_Tools.Count();
}
//-----------------------------------------------------------------------------
// Purpose: Accessor for iterating tools.
//-----------------------------------------------------------------------------
CBaseTool *CToolManager::GetTool(int nIndex)
{
return m_Tools.Element(nIndex);
}
// get the tool manager for the current active document:
CToolManager *ToolManager();
#endif // TOOLMANAGER_H

280
hammer/ToolMaterial.cpp Normal file
View File

@@ -0,0 +1,280 @@
//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============//
//
// Purpose: Handles the selection of faces and material application.
//
// TODO: consider making face selection a mode of the selection tool
//
// $NoKeywords: $
//=============================================================================//
#include "stdafx.h"
#include "FaceEditSheet.h"
#include "History.h"
#include "MainFrm.h"
#include "MapDoc.h"
#include "MapSolid.h"
#include "MapView2D.h"
#include "MapView3D.h"
#include "StatusBarIDs.h"
#include "ToolManager.h"
#include "ToolMaterial.h"
#include "Options.h"
// memdbgon must be the last include file in a .cpp file!!!
#include <tier0/memdbgon.h>
//-----------------------------------------------------------------------------
// Purpose: Called when this tool is deactivated in favor of another tool.
// Input : eNewTool - The tool that is being activated.
//-----------------------------------------------------------------------------
void CToolMaterial::OnDeactivate()
{
if ( m_pDocument->GetTools()->GetActiveToolID() != TOOL_FACEEDIT_DISP )
{
// Clear the selected faces when we are deactivated.
m_pDocument->SelectFace(NULL, 0, scClear );
}
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : pView -
// nFlags -
// point -
// Output : Returns true if the message was handled, false if not.
//-----------------------------------------------------------------------------
bool CToolMaterial::OnLMouseDown2D(CMapView2D *pView, UINT nFlags, const Vector2D &vPoint)
{
if (nFlags & MK_CONTROL)
{
//
// CONTROL is down, perform selection only.
//
pView->SelectAt( vPoint, false, true);
}
else
{
pView->SelectAt( vPoint, true, true);
}
return (true);
}
//-----------------------------------------------------------------------------
// Purpose: Handles left mouse button down events in the 3D view.
// Input : Per CWnd::OnLButtonDown.
// Output : Returns true if the message was handled, false if not.
//-----------------------------------------------------------------------------
bool CToolMaterial::OnLMouseDown3D(CMapView3D *pView, UINT nFlags, const Vector2D &vPoint)
{
CMapDoc *pDoc = pView->GetMapDoc();
if (pDoc == NULL)
{
return false;
}
bool bShift = ((GetAsyncKeyState(VK_SHIFT) & 0x8000) != 0);
ULONG ulFace;
CMapClass *pObject = pView->NearestObjectAt( vPoint, ulFace);
if ((pObject != NULL) && (pObject->IsMapClass(MAPCLASS_TYPE(CMapSolid))))
{
CMapSolid *pSolid = (CMapSolid *)pObject;
int cmd = scToggle | scClear;
// No clear if CTRL pressed.
if (GetAsyncKeyState(VK_CONTROL) & 0x8000)
{
cmd &= ~scClear;
}
// If they are holding down SHIFT, select the entire solid.
if (bShift)
{
pDoc->SelectFace(pSolid, -1, cmd);
}
// Otherwise, select a single face.
else
{
pDoc->SelectFace(pSolid, ulFace, cmd);
}
}
return true;
}
//-----------------------------------------------------------------------------
// Purpose: Handles right mouse button down events in the 3D view.
// Input : Per CWnd::OnRButtonDown.
// Output : Returns true if the message was handled, false if not.
//-----------------------------------------------------------------------------
bool CToolMaterial::OnRMouseDown3D(CMapView3D *pView, UINT nFlags, const Vector2D &vPoint)
{
BOOL bShift = ((GetAsyncKeyState(VK_SHIFT) & 0x8000) != 0);
BOOL bEdgeAlign = ((GetAsyncKeyState(VK_MENU) & 0x8000) != 0);
ULONG ulFace;
CMapClass *pObject = pView->NearestObjectAt( vPoint, ulFace);
if (pObject != NULL)
{
if (pObject->IsMapClass(MAPCLASS_TYPE(CMapSolid)))
{
CMapSolid *pSolid = (CMapSolid *)pObject;
GetHistory()->MarkUndoPosition(NULL, "Apply texture");
GetHistory()->Keep(pSolid);
// Setup the flags.
int cmdFlags = 0;
if (bEdgeAlign)
{
cmdFlags |= CFaceEditSheet::cfEdgeAlign;
}
// If we're in a lightmap grid preview window, only apply the lightmap scale.
int eMode;
if (pView->GetDrawType() == VIEW3D_LIGHTMAP_GRID)
{
eMode = CFaceEditSheet::ModeApplyLightmapScale;
}
else
{
eMode = CFaceEditSheet::ModeApplyAll;
}
// If they are holding down the shift key, apply to the entire solid.
if (bShift)
{
int nFaces = pSolid->GetFaceCount();
for(int i = 0; i < nFaces; i++)
{
GetMainWnd()->m_pFaceEditSheet->ClickFace(pSolid, i, cmdFlags, eMode);
}
}
// If not, apply to a single face.
else
{
GetMainWnd()->m_pFaceEditSheet->ClickFace(pSolid, ulFace, cmdFlags, eMode);
}
}
}
return true;
}
//-----------------------------------------------------------------------------
// Purpose: Handles the mouse move message in the 3D view.
// Input : Per CWnd::OnMouseMove.
//-----------------------------------------------------------------------------
bool CToolMaterial::OnMouseMove3D(CMapView3D *pView, UINT nFlags, const Vector2D &vPoint)
{
//
// Manage the cursor.
//
static HCURSOR hcurFacePaint = 0;
if (!hcurFacePaint)
{
hcurFacePaint = LoadCursor(AfxGetInstanceHandle(), MAKEINTRESOURCE(IDC_FACEPAINT));
}
SetCursor(hcurFacePaint);
return true;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CToolMaterial::UpdateStatusBar()
{
CString str;
str.Format("%d faces selected", GetMainWnd()->m_pFaceEditSheet->GetFaceListCount() );
SetStatusText(SBI_SELECTION, str);
SetStatusText(SBI_SIZE, "");
}
//-----------------------------------------------------------------------------
// Purpose: Handles the key down event in the 3D view.
// Input : Per CWnd::OnKeyDown.
// Output : Returns true if the message was handled, false if not.
//-----------------------------------------------------------------------------
bool CToolMaterial::OnKeyDown3D( CMapView3D *pView, UINT nChar, UINT nRepCnt, UINT nFlags )
{
// TO DO: undo?
// justify (SHIFT?)
CMapDoc *pDoc = pView->GetMapDoc();
if ( pDoc == NULL )
{
return false;
}
if ( nChar == VK_UP || nChar == VK_DOWN || nChar == VK_LEFT || nChar == VK_RIGHT )
{
// Bail out if the user doesn't have Nudging enabled.
if ( !Options.view2d.bNudge )
{
return false;
}
CFaceEditSheet *pSheet = GetMainWnd()->m_pFaceEditSheet;
if( pSheet )
{
// Check for a face list.
int nFaceCount = pSheet->GetFaceListCount();
if( nFaceCount == 0 )
{
return false;
}
int nGridSize = m_pDocument->GetGridSpacing();
bool bCtrlDown = ( ( GetAsyncKeyState( VK_CONTROL ) & 0x8000 ) != 0 );
if ( bCtrlDown )
{
nGridSize = 1;
}
for( int ndxface = 0; ndxface < nFaceCount; ndxface++ )
{
CMapFace *pFace;
pFace = pSheet->GetFaceListDataFace( ndxface );
TEXTURE &t = pFace->texture;
if ( nChar == VK_UP )
{
t.VAxis[ 3 ] = ( (int)( t.VAxis[ 3 ] + nGridSize ) % 1024 );
}
else if ( nChar == VK_DOWN )
{
t.VAxis[ 3 ] = ( (int)( t.VAxis[ 3 ] - nGridSize ) % 1024 );
}
else if ( nChar == VK_LEFT )
{
t.UAxis[ 3 ] = ( (int)( t.UAxis[ 3 ] + nGridSize ) % 1024 );
}
else
{
t.UAxis[ 3 ] = ( (int)( t.UAxis[ 3 ] - nGridSize ) % 1024 );
}
pFace->CalcTextureCoords();
pSheet->m_MaterialPage.UpdateDialogData( pFace );
}
pDoc->SetModifiedFlag();
return true;
}
}
return false;
}

37
hammer/ToolMaterial.h Normal file
View File

@@ -0,0 +1,37 @@
//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//=============================================================================//
#ifndef TOOLMATERIAL_H
#define TOOLMATERIAL_H
#ifdef _WIN32
#pragma once
#endif
#include "ToolInterface.h"
class CToolMaterial : public CBaseTool
{
//
// CBaseTool implementation.
//
virtual ToolID_t GetToolID(void) { return TOOL_FACEEDIT_MATERIAL; }
virtual bool OnLMouseDown2D(CMapView2D *pView, UINT nFlags, const Vector2D &vPoint);
virtual bool OnKeyDown3D(CMapView3D *pView, UINT nChar, UINT nRepCnt, UINT nFlags);
virtual bool OnLMouseDown3D(CMapView3D *pView, UINT nFlags, const Vector2D &vPoint);
virtual bool OnRMouseDown3D(CMapView3D *pView, UINT nFlags, const Vector2D &vPoint);
virtual bool OnMouseMove3D(CMapView3D *pView, UINT nFlags, const Vector2D &vPoint);
virtual void UpdateStatusBar();
virtual void OnDeactivate();
};
#endif // TOOLMATERIAL_H

2107
hammer/ToolMorph.cpp Normal file

File diff suppressed because it is too large Load Diff

200
hammer/ToolMorph.h Normal file
View File

@@ -0,0 +1,200 @@
//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//=============================================================================//
#ifndef MORPH3D_H
#define MORPH3D_H
#ifdef _WIN32
#pragma once
#endif
#include "MapClass.h" // dvs: For CMapObjectList
#include "Box3D.h"
#include "SSolid.h"
#include "Resource.h"
#include "ScaleVerticesDlg.h"
#include "ToolInterface.h"
#include "mathlib/vector.h"
class IMesh;
class Morph3D;
class CRender2D;
class CRender3D;
const SSHANDLE SSH_SCALEORIGIN = 0xffff0L;
typedef struct
{
CMapSolid *pMapSolid;
CSSolid *pStrucSolid;
SSHANDLE ssh;
} MORPHHANDLE;
class Morph3D : public Box3D
{
public:
Morph3D();
virtual ~Morph3D();
BOOL IsMorphing(CMapSolid *pSolid, CSSolid **pStrucSolidRvl = NULL);
bool SplitFace();
bool CanSplitFace();
void SelectHandle(MORPHHANDLE *pInfo, UINT cmd = scSelect);
void SelectHandle2D( CMapView2D *pView, MORPHHANDLE *pInfo, UINT cmd = scSelect);
void DeselectHandle(MORPHHANDLE *pInfo);
void MoveSelectedHandles(const Vector &Delta);
int GetSelectedHandleCount(void) { return m_SelectedHandles.Count(); }
void GetSelectedCenter(Vector& pt);
SSHANDLETYPE GetSelectedType() { return m_SelectedType; }
bool IsSelected(MORPHHANDLE &mh);
void SelectObject(CMapSolid *pSolid, UINT cmd = scSelect);
bool SelectAt( CMapView *pView, UINT nFlags, const Vector2D &vPoint );
void GetMorphBounds(Vector &mins, Vector &maxs, bool bReset);
// Toggle mode - vertex & edge, vertex, edge.
void ToggleMode();
void OnScaleCmd(BOOL bReInit = FALSE);
void UpdateScale();
BOOL IsScaling() { return m_bScaling; }
void GetMorphingObjects(CUtlVector<CMapClass *> &List);
inline int GetObjectCount(void);
inline CSSolid *GetObject(int pos);
//
// Tool3D implementation.
//
virtual bool IsEmpty() { return !m_StrucSolids.Count() && !m_bBoxSelecting; }
virtual void SetEmpty();
virtual void FinishTranslation(bool bSave);
virtual unsigned int GetConstraints(unsigned int nKeyFlags);
//
// CBaseTool implementation.
//
virtual void OnActivate();
virtual void OnDeactivate();
virtual ToolID_t GetToolID(void) { return TOOL_MORPH; }
virtual bool CanDeactivate( void );
virtual bool OnKeyDown2D(CMapView2D *pView, UINT nChar, UINT nRepCnt, UINT nFlags);
virtual bool OnChar2D(CMapView2D *pView, UINT nChar, UINT nRepCnt, UINT nFlags);
virtual bool OnLMouseDown2D(CMapView2D *pView, UINT nFlags, const Vector2D &vPoint);
virtual bool OnLMouseUp2D(CMapView2D *pView, UINT nFlags, const Vector2D &vPoint);
virtual bool OnMouseMove2D(CMapView2D *pView, UINT nFlags, const Vector2D &vPoint);
virtual bool OnLMouseDown3D(CMapView3D *pView, UINT nFlags, const Vector2D &vPoint);
virtual bool OnLMouseUp3D(CMapView3D *pView, UINT nFlags, const Vector2D &vPoint);
virtual bool OnKeyDown3D(CMapView3D *pView, UINT nChar, UINT nRepCnt, UINT nFlags);
virtual bool OnMouseMove3D(CMapView3D *pView, UINT nFlags, const Vector2D &vPoint);
virtual void RenderTool2D(CRender2D *pRender);
virtual void RenderTool3D(CRender3D *pRender);
private:
void OnEscape(void);
bool NudgeHandles(CMapView *pView, UINT nChar, bool bSnap);
bool MorphHitTest(CMapView *pView, const Vector2D &vPoint, MORPHHANDLE *pInfo);
void GetHandlePos(MORPHHANDLE *pInfo, Vector& pt);
SSHANDLE Get2DMatches(CMapView2D *pView, CSSolid *pStrucSolid, SSHANDLEINFO &hi, CUtlVector<SSHANDLE>*pSimilarList = NULL);
void StartTranslation(CMapView *pView, const Vector2D &vPoint, MORPHHANDLE *pInfo );
void RenderSolid3D(CRender3D *pRender, CSSolid *pSolid);
//
// Tool3D implementations.
//
int HitTest(CMapView *pView, const Vector2D &ptClient, bool bTestHandles = false);
virtual bool UpdateTranslation( const Vector &pos, UINT uFlags );
bool StartBoxSelection( CMapView *pView, const Vector2D &vPoint, const Vector &vStart);
void SelectInBox();
void EndBoxSelection();
bool IsBoxSelecting() { return m_bBoxSelecting; }
bool CanDeselectList( void );
// list of active Structured Solids:
CUtlVector<CSSolid*> m_StrucSolids;
// list of selected nodes:
CUtlVector<MORPHHANDLE> m_SelectedHandles;
// type of selected handles:
SSHANDLETYPE m_SelectedType;
// main morph handle:
MORPHHANDLE m_MorphHandle;
Vector m_OrigHandlePos;
// morph bounds:
BoundBox m_MorphBounds;
// handle mode:
enum
{
hmBoth = 0x01 | 0x02,
hmVertex = 0x01,
hmEdge = 0x02
};
bool m_bLButtonDownControlState;
Vector2D m_vLastMouseMovement;
bool m_bHit;
MORPHHANDLE m_DragHandle; // The morph handle that we are dragging.
bool m_bMorphing;
bool m_bMovingSelected; // not moving them yet - might just select this
int m_HandleMode;
bool m_bBoxSelecting;
bool m_bScaling;
bool m_bUpdateOrg;
CScaleVerticesDlg m_ScaleDlg;
Vector *m_pOrigPosList;
Vector m_ScaleOrg;
};
//-----------------------------------------------------------------------------
// Purpose: Returns the number of solids selected for morphing.
//-----------------------------------------------------------------------------
inline int Morph3D::GetObjectCount(void)
{
return(m_StrucSolids.Count());
}
//-----------------------------------------------------------------------------
// Purpose: Iterates the selected solids.
//-----------------------------------------------------------------------------
inline CSSolid *Morph3D::GetObject(int pos)
{
return(m_StrucSolids.Element(pos));
}
#endif // MORPH3D_H

592
hammer/ToolOverlay.cpp Normal file
View File

@@ -0,0 +1,592 @@
//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//=============================================================================//
#include <stdafx.h>
#include "MapWorld.h"
#include "GlobalFunctions.h"
#include "MainFrm.h"
#include "ToolOverlay.h"
#include "MapDoc.h"
#include "History.h"
#include "CollisionUtils.h"
#include "cmodel.h"
#include "MapView3D.h"
#include "MapView2D.h"
#include "MapSolid.h"
#include "Camera.h"
#include "ObjectProperties.h" // FIXME: For ObjectProperties::RefreshData
#include "Selection.h"
// memdbgon must be the last include file in a .cpp file!!!
#include <tier0/memdbgon.h>
#define OVERLAY_TOOL_SNAP_DISTANCE 35.0f
//-----------------------------------------------------------------------------
// Purpose: Constructor
//-----------------------------------------------------------------------------
CToolOverlay::CToolOverlay()
{
m_bDragging = false;
m_pActiveOverlay = NULL;
}
//-----------------------------------------------------------------------------
// Purpose: Destructor
//-----------------------------------------------------------------------------
CToolOverlay::~CToolOverlay()
{
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CToolOverlay::OnActivate()
{
m_bDragging = false;
m_pActiveOverlay = NULL;
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CToolOverlay::OnDeactivate()
{
}
//-----------------------------------------------------------------------------
// 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 CToolOverlay::OnKeyDown2D(CMapView2D *pView, UINT nChar, UINT nRepCnt, UINT nFlags)
{
switch (nChar)
{
case VK_ESCAPE:
{
ToolManager()->SetTool(TOOL_POINTER);
return true;
}
}
return false;
}
//-----------------------------------------------------------------------------
// 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 CToolOverlay::OnKeyDown3D(CMapView3D *pView, UINT nChar, UINT nRepCnt, UINT nFlags)
{
switch (nChar)
{
case VK_ESCAPE:
{
ToolManager()->SetTool(TOOL_POINTER);
return true;
}
}
return false;
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
bool CToolOverlay::OnLMouseUp3D( CMapView3D *pView, UINT nFlags, const Vector2D &vPoint )
{
// Post drag events.
PostDrag();
// Update the entity properties dialog.
GetMainWnd()->pObjectProperties->MarkDataDirty();
return true;
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
bool CToolOverlay::OnLMouseDown3D( CMapView3D *pView, UINT nFlags, const Vector2D &vPoint )
{
// Handle the overlay "handle" selection.
if ( HandleSelection( pView, vPoint ) )
{
PreDrag();
return true;
}
// Handle adding and removing overlay entities from the selection list.
OverlaySelection( pView, nFlags, vPoint );
// Handle the overlay creation and placement (if we hit a solid).
ULONG ulFace;
CMapClass *pObject = NULL;
if ( ( pObject = pView->NearestObjectAt( vPoint, ulFace ) ) != NULL )
{
CMapSolid *pSolid = dynamic_cast<CMapSolid*>( pObject );
if ( pSolid )
{
return CreateOverlay( pSolid, ulFace, pView, vPoint );
}
}
return true;
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
bool CToolOverlay::OnMouseMove3D( CMapView3D *pView, UINT nFlags, const Vector2D &vPoint )
{
if ( m_bDragging )
{
bool bShift = ( ( GetKeyState( VK_SHIFT ) & 0x8000 ) != 0 );
// Build the ray and drag the overlay handle to the impact point.
const CCamera *pCamera = pView->GetCamera();
if ( pCamera )
{
Vector vecStart, vecEnd;
pView->BuildRay( vPoint, vecStart, vecEnd );
OnDrag( vecStart, vecEnd, bShift );
}
}
return true;
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
bool CToolOverlay::CreateOverlay( CMapSolid *pSolid, ULONG iFace, CMapView3D *pView, Vector2D point )
{
// Build a ray to trace against the face that they clicked on to
// find the point of intersection.
Vector vecStart, vecEnd;
pView->BuildRay( point, vecStart, vecEnd );
Vector vecHitPos, vecHitNormal;
CMapFace *pFace = pSolid->GetFace( iFace );
if( pFace->TraceLine( vecHitPos, vecHitNormal, vecStart, vecEnd ) )
{
// Create and initialize the "entity" --> "overlay."
CMapEntity *pEntity = new CMapEntity;
pEntity->SetKeyValue( "material", GetDefaultTextureName() );
pEntity->SetPlaceholder( TRUE );
pEntity->SetOrigin( vecHitPos );
pEntity->SetClass( "info_overlay" );
// Add the entity to the world.
m_pDocument->AddObjectToWorld( pEntity );
// Setup "history."
GetHistory()->MarkUndoPosition( NULL, "Create Overlay" );
GetHistory()->KeepNew( pEntity );
// Initialize the overlay.
InitOverlay( pEntity, pFace );
pEntity->CalcBounds( TRUE );
// Add to selection list.
m_pDocument->SelectObject( pEntity, scSelect );
m_bEmpty = false;
// Set modified and update views.
m_pDocument->SetModifiedFlag();
m_pShoreline = NULL;
return true;
}
return false;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CToolOverlay::InitOverlay( CMapEntity *pEntity, CMapFace *pFace )
{
// Valid face?
if ( !pFace )
return;
const CMapObjectList *pChildren = pEntity->GetChildren();
FOR_EACH_OBJ( *pChildren, pos )
{
CMapClass *pMapClassObj = (CUtlReference< CMapClass >)pChildren->Element(pos);
CMapOverlay *pOverlay = dynamic_cast<CMapOverlay*>( pMapClassObj );
if ( pOverlay )
{
pOverlay->Basis_Init( pFace );
pOverlay->Handles_Init( pFace );
pOverlay->SideList_Init( pFace );
pOverlay->SetOverlayType( OVERLAY_TYPE_GENERIC );
pOverlay->SetLoaded( true );
pOverlay->CalcBounds( true );
}
}
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CToolOverlay::OverlaySelection( CMapView3D *pView, UINT nFlags, const Vector2D &vPoint )
{
CMapObjectList aSelectionList;
m_pDocument->GetSelection()->ClearHitList();
// Find out how many (and what) map objects are under the point clicked on.
HitInfo_t Objects[MAX_PICK_HITS];
int nHits = pView->ObjectsAt( vPoint, Objects, sizeof( Objects ) / sizeof( Objects[0] ) );
if ( nHits != 0 )
{
// We now have an array of pointers to CMapAtoms. Any that can be upcast to CMapClass objects?
for ( int iHit = 0; iHit < nHits; ++iHit )
{
CMapClass *pMapClass = dynamic_cast<CMapClass*>( Objects[iHit].pObject );
if ( pMapClass )
{
aSelectionList.AddToTail( pMapClass );
}
}
}
// Did we hit anything?
if ( !aSelectionList.Count() )
{
m_pDocument->SelectFace( NULL, 0, scClear );
m_pDocument->SelectObject( NULL, scClear|scSaveChanges );
SetEmpty();
return;
}
// Find overlays.
bool bUpdateViews = false;
SelectMode_t eSelectMode = m_pDocument->GetSelection()->GetMode();
FOR_EACH_OBJ( aSelectionList, pos )
{
CMapClass *pObject = aSelectionList.Element( pos );
CMapClass *pHitObject = pObject->PrepareSelection( eSelectMode );
if ( pHitObject )
{
if ( pHitObject->IsMapClass( MAPCLASS_TYPE( CMapEntity ) ) )
{
const CMapObjectList *pChildren = pHitObject->GetChildren();
FOR_EACH_OBJ( *pChildren, pos2 )
{
CMapClass *pMapClassObj = (CUtlReference< CMapClass >)pChildren->Element(pos2);
CMapOverlay *pOverlay = dynamic_cast<CMapOverlay*>( pMapClassObj );
if ( pOverlay )
{
m_pDocument->GetSelection()->AddHit( pHitObject );
m_bEmpty = false;
UINT cmd = scClear | scSelect | scSaveChanges;
if (nFlags & MK_CONTROL)
{
cmd = scToggle;
}
m_pDocument->SelectObject( pHitObject, cmd );
bUpdateViews = true;
}
}
}
}
}
// Update the views.
if ( bUpdateViews )
{
m_pDocument->UpdateAllViews( MAPVIEW_UPDATE_OBJECTS );
}
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
bool CToolOverlay::OnContextMenu2D( CMapView2D *pView, UINT nFlags, const Vector2D &vPoint )
{
static CMenu menu, menuOverlay;
static bool bInit = false;
if ( !bInit )
{
// Create the menu.
menu.LoadMenu( IDR_POPUPS );
menuOverlay.Attach( ::GetSubMenu( menu.m_hMenu, 6 ) );
bInit = true;
}
if ( !pView->PointInClientRect(vPoint) )
return false;
if (!IsEmpty())
{
if ( HitTest( pView, vPoint, false ) )
{
CPoint ptScreen( vPoint.x,vPoint.y);
pView->ClientToScreen(&ptScreen);
menuOverlay.TrackPopupMenu( TPM_LEFTBUTTON | TPM_RIGHTBUTTON | TPM_LEFTALIGN, ptScreen.x, ptScreen.y, pView );
}
}
return true;
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
bool CToolOverlay::UpdateTranslation( const Vector &vUpdate, UINT nFlags)
{
// if( m_bBoxSelecting )
// return Box3D::UpdateTranslation( pt, nFlags );
return true;
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CToolOverlay::HandlesReset( void )
{
// Go through selection list and reset overlay handles.
const CMapObjectList *pSelection = m_pDocument->GetSelection()->GetList();
for( int iSelection = 0; iSelection < pSelection->Count(); ++iSelection )
{
CMapClass *pMapClass = (CUtlReference< CMapClass >)pSelection->Element( iSelection );
if ( pMapClass && pMapClass->IsMapClass( MAPCLASS_TYPE( CMapEntity ) ) )
{
const CMapObjectList *pChildren = pMapClass->GetChildren();
FOR_EACH_OBJ( *pChildren, pos )
{
CMapClass *pMapClassCast = (CUtlReference< CMapClass >)pChildren->Element( pos );
CMapOverlay *pOverlay = dynamic_cast<CMapOverlay*>( pMapClassCast );
if ( pOverlay )
{
pOverlay->HandlesReset();
break;
}
}
}
}
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
bool CToolOverlay::HandleSelection( CMapView *pView, const Vector2D &vPoint )
{
// Reset the hit overlay.
m_pActiveOverlay = NULL;
// Go through selection list and test all overlay's handles and set the
// "hit" overlay current.
const CMapObjectList *pSelection = m_pDocument->GetSelection()->GetList();
for ( int iSelection = 0; iSelection < pSelection->Count(); ++iSelection )
{
CMapClass *pMapClass = (CUtlReference< CMapClass >)pSelection->Element( iSelection );
if ( pMapClass && pMapClass->IsMapClass( MAPCLASS_TYPE( CMapEntity ) ) )
{
const CMapObjectList *pChildren = pMapClass->GetChildren();
FOR_EACH_OBJ( *pChildren, pos )
{
CMapClass *pMapClassCast = (CUtlReference< CMapClass >)pChildren->Element(pos);
CMapOverlay *pOverlay = dynamic_cast<CMapOverlay*>( pMapClassCast );
if ( pOverlay && pOverlay->IsSelected() )
{
if ( pOverlay->HandlesHitTest( pView, vPoint ) )
{
m_pActiveOverlay = pOverlay;
break;
}
}
}
}
}
if ( !m_pActiveOverlay )
return false;
return true;
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
bool CToolOverlay::HandleSnap( CMapOverlay *pOverlay, Vector &vecHandlePt )
{
Vector vecTmp;
for ( int i = 0; i < OVERLAY_HANDLES_COUNT; i++ )
{
pOverlay->GetHandlePos( i, vecTmp );
vecTmp -= vecHandlePt;
float flDist = vecTmp.Length();
if ( flDist < OVERLAY_TOOL_SNAP_DISTANCE )
{
// Snap!
pOverlay->GetHandlePos( i, vecHandlePt );
return true;
}
}
return false;
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
bool CToolOverlay::HandleInBBox( CMapOverlay *pOverlay, Vector const &vecHandlePt )
{
Vector vecMin, vecMax;
pOverlay->GetCullBox( vecMin, vecMax );
for ( int iAxis = 0; iAxis < 3; iAxis++ )
{
vecMin[iAxis] -= OVERLAY_TOOL_SNAP_DISTANCE;
vecMax[iAxis] += OVERLAY_TOOL_SNAP_DISTANCE;
if( ( vecHandlePt[iAxis] < vecMin[iAxis] ) || ( vecHandlePt[iAxis] > vecMax[iAxis] ) )
return false;
}
return true;
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CToolOverlay::SnapHandle( Vector &vecHandlePt )
{
CMapWorld *pWorld = GetActiveWorld();
if ( !pWorld )
return;
EnumChildrenPos_t pos;
CMapClass *pChild = pWorld->GetFirstDescendent( pos );
while ( pChild )
{
CMapEntity *pEntity = dynamic_cast<CMapEntity*>( pChild );
if ( pEntity )
{
const CMapObjectList *pChildren = pEntity->GetChildren();
FOR_EACH_OBJ( *pChildren, pos )
{
CMapClass *pMapClassCast = (CUtlReference< CMapClass >)pChildren->Element(pos);
CMapOverlay *pOverlay = dynamic_cast<CMapOverlay*>( pMapClassCast );
if ( pOverlay && pOverlay != m_pActiveOverlay && pOverlay->IsSelected() )
{
// Intersection test and attempt to snap
if ( HandleInBBox( pOverlay, vecHandlePt ) )
{
if ( HandleSnap( pOverlay, vecHandlePt ) )
return;
}
}
}
}
pChild = pWorld->GetNextDescendent( pos );
}
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CToolOverlay::OnDrag( Vector const &vecRayStart, Vector const &vecRayEnd, bool bShift )
{
// Get the current overlay.
CMapOverlay *pOverlay = m_pActiveOverlay;
if ( !pOverlay )
return;
// Get a list of faces and test for "impact."
Vector vecImpact( 0.0f, 0.0f, 0.0f );
Vector vecImpactNormal( 0.0f, 0.0f, 0.0f );
CMapFace *pFace = NULL;
int nFaceCount = pOverlay->GetFaceCount();
int iFace;
for ( iFace = 0; iFace < nFaceCount; iFace++ )
{
pFace = pOverlay->GetFace( iFace );
if ( pFace )
{
if ( pFace->TraceLineInside( vecImpact, vecImpactNormal, vecRayStart, vecRayEnd ) )
break;
}
}
// Test for impact (face index = count mean no impact).
if ( iFace == nFaceCount )
return;
if ( bShift )
{
SnapHandle( vecImpact );
}
// Pass the new handle position to the overlay.
pOverlay->HandlesDragTo( vecImpact, pFace );
m_pDocument->UpdateAllViews( MAPVIEW_UPDATE_ONLY_3D );
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CToolOverlay::PreDrag( void )
{
m_bDragging = true;
SetupHandleDragUndo();
}
//-----------------------------------------------------------------------------
// Purpose: Renders the cordon tool in the 3D view.
//-----------------------------------------------------------------------------
void CToolOverlay::RenderTool3D(CRender3D *pRender)
{
// TODO render tool handles here and not in overlay rendering code
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CToolOverlay::PostDrag( void )
{
if ( !m_bDragging )
return;
m_bDragging = false;
// Get the current overlay.
CMapOverlay *pOverlay = m_pActiveOverlay;
if ( pOverlay )
{
pOverlay->DoClip();
pOverlay->CenterEntity();
pOverlay->PostUpdate( Notify_Changed );
m_pDocument->UpdateAllViews( MAPVIEW_UPDATE_OBJECTS );
}
// Reset the overlay handles.
HandlesReset();
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CToolOverlay::SetupHandleDragUndo( void )
{
// Get the current overlay.
CMapOverlay *pOverlay = m_pActiveOverlay;
if ( pOverlay )
{
CMapEntity *pEntity = ( CMapEntity* )pOverlay->GetParent();
if ( pEntity )
{
// Setup for drag undo.
GetHistory()->MarkUndoPosition( m_pDocument->GetSelection()->GetList(), "Drag Overlay Handle" );
GetHistory()->Keep( ( CMapClass* )pEntity );
}
}
}

79
hammer/ToolOverlay.h Normal file
View File

@@ -0,0 +1,79 @@
//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//=============================================================================//
#ifndef OVERLAY3D_H
#define OVERLAY3D_H
#pragma once
#include <afxwin.h>
#include "Box3D.h"
#include "ToolInterface.h"
#include "MapOverlay.h"
#include "ToolManager.h"
class CMapDoc;
struct Shoreline_t;
class CToolOverlay : public Box3D
{
public:
//=========================================================================
//
// Constructur/Destructor
//
CToolOverlay();
~CToolOverlay();
//=========================================================================
//
// CBaseTool virtual implementations
//
ToolID_t GetToolID( void ) { return TOOL_OVERLAY; }
void OnActivate();
void OnDeactivate();
virtual bool OnKeyDown2D(CMapView2D *pView, UINT nChar, UINT nRepCnt, UINT nFlags);
virtual bool OnKeyDown3D(CMapView3D *pView, UINT nChar, UINT nRepCnt, UINT nFlags);
bool OnLMouseUp3D( CMapView3D *pView, UINT nFlags, const Vector2D &vPoint );
bool OnLMouseDown3D( CMapView3D *pView, UINT nFlags, const Vector2D &vPoint );
bool OnMouseMove3D( CMapView3D *pView, UINT nFlags, const Vector2D &vPoint );
bool OnContextMenu2D( CMapView2D *pView, UINT nFlags, const Vector2D &vPoint );
void RenderTool3D(CRender3D *pRender);
protected:
bool UpdateTranslation( const Vector &vUpdate, UINT = 0 );
private:
bool HandleSelection( CMapView *pView, const Vector2D &vPoint );
void OverlaySelection( CMapView3D *pView, UINT nFlags, const Vector2D &vPoint );
bool CreateOverlay( CMapSolid *pSolid, ULONG iFace, CMapView3D *pView, Vector2D point );
void InitOverlay( CMapEntity *pEntity, CMapFace *pFace );
void OnDrag( Vector const &vecRayStart, Vector const &vecRayEnd, bool bShift );
void PreDrag( void );
void PostDrag( void );
void SetupHandleDragUndo( void );
void HandlesReset( void );
void SnapHandle( Vector &vecHandlePt );
bool HandleInBBox( CMapOverlay *pOverlay, Vector const &vecHandlePt );
bool HandleSnap( CMapOverlay *pOverlay, Vector &vecHandlePt );
private:
bool m_bDragging; // Are we dragging overlay handles?
Shoreline_t *m_pShoreline; //
CMapOverlay *m_pActiveOverlay; // The overlay currently being acted upon
};
#endif // OVERLAY3D_H

174
hammer/ToolPickAngles.cpp Normal file
View File

@@ -0,0 +1,174 @@
//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============//
//
// Purpose: Tool used for point-and-click setting of angles.
//
//=============================================================================//
#include "stdafx.h"
#include "resource.h"
#include "ToolPickAngles.h"
#include "MapView3D.h"
#include "MapSolid.h"
// memdbgon must be the last include file in a .cpp file!!!
#include <tier0/memdbgon.h>
//-----------------------------------------------------------------------------
// Purpose: Constructor. Inits data members.
//-----------------------------------------------------------------------------
CToolPickAngles::CToolPickAngles(void)
{
m_pNotifyTarget = NULL;
}
//-----------------------------------------------------------------------------
// Purpose: Destructor.
//-----------------------------------------------------------------------------
CToolPickAngles::~CToolPickAngles(void)
{
}
//-----------------------------------------------------------------------------
// Purpose: Handles the left mouse button up message in the 3D view.
// Input : pView - The view that the event occurred in.
// nFlags - Flags per the Windows mouse message.
// point - Point in client coordinates where the event occurred.
// Output : Returns true if the message was handled by the tool, false if not.
//-----------------------------------------------------------------------------
bool CToolPickAngles::OnLMouseUp3D(CMapView3D *pView, UINT nFlags, const Vector2D &vPoint)
{
return true;
}
//-----------------------------------------------------------------------------
// Purpose: Handles the left mouse button up message in the 3D view.
// Input : pView - The view that the event occurred in.
// nFlags - Flags per the Windows mouse message.
// point - Point in client coordinates where the event occurred.
// Output : Returns true if the message was handled by the tool, false if not.
//-----------------------------------------------------------------------------
bool CToolPickAngles::OnLMouseDown3D(CMapView3D *pView, UINT nFlags, const Vector2D &vPoint)
{
unsigned long ulFace;
CMapClass *pObject = pView->NearestObjectAt( vPoint, ulFace);
if (pObject != NULL)
{
CMapClass *pSelObject = pObject->PrepareSelection(selectObjects);
CMapEntity *pEntity = dynamic_cast <CMapEntity *>(pSelObject);
if (pEntity != NULL)
{
//
// We clicked on an entity.
//
if (m_pNotifyTarget)
{
Vector vecCenter;
pEntity->GetBoundsCenter(vecCenter);
m_pNotifyTarget->OnNotifyPickAngles(vecCenter);
}
}
else
{
CMapSolid *pSolid = dynamic_cast <CMapSolid *> (pObject);
if (pSolid == NULL)
{
return true;
}
//
// Build a ray to trace against the face that they clicked on to
// find the point of intersection.
//
Vector Start,End;
pView->GetCamera()->BuildRay( vPoint, Start, End);
Vector HitPos;
Vector HitNormal;
CMapFace *pFace = pSolid->GetFace(ulFace);
if (pFace->TraceLine(HitPos, HitNormal, Start, End))
{
if (m_pNotifyTarget)
{
m_pNotifyTarget->OnNotifyPickAngles(HitPos);
}
}
}
}
return true;
}
//-----------------------------------------------------------------------------
// Purpose: Handles the left mouse button double click message in the 3D view.
// Input : pView - The view that the event occurred in.
// nFlags - Flags per the Windows mouse message.
// point - Point in client coordinates where the event occurred.
// Output : Returns true if the message was handled by the tool, false if not.
//-----------------------------------------------------------------------------
bool CToolPickAngles::OnLMouseDblClk3D(CMapView3D *pView, UINT nFlags, const Vector2D &vPoint)
{
return true;
}
//-----------------------------------------------------------------------------
// Purpose: Handles the right mouse button up message in the 3D view.
// Input : pView - The view that the event occurred in.
// nFlags - Flags per the Windows mouse message.
// point - Point in client coordinates where the event occurred.
// Output : Returns true if the message was handled by the tool, false if not.
//-----------------------------------------------------------------------------
bool CToolPickAngles::OnRMouseUp3D(CMapView3D *pView, UINT nFlags, const Vector2D &vPoint)
{
return true;
}
//-----------------------------------------------------------------------------
// Purpose: Handles the mouse button up message in the 3D view.
// Input : pView - The view that the event occurred in.
// nFlags - Flags per the Windows mouse message.
// point - Point in client coordinates where the event occurred.
// Output : Returns true if the message was handled by the tool, false if not.
//-----------------------------------------------------------------------------
bool CToolPickAngles::OnRMouseDown3D(CMapView3D *pView, UINT nFlags, const Vector2D &vPoint)
{
return true;
}
//-----------------------------------------------------------------------------
// Purpose: Handles the mouse move message in the 3D view.
// Input : pView - The view that the event occurred in.
// nFlags - Flags per the Windows mouse message.
// point - Point in client coordinates where the event occurred.
// Output : Returns true if the message was handled by the tool, false if not.
//-----------------------------------------------------------------------------
bool CToolPickAngles::OnMouseMove3D(CMapView3D *pView, UINT nFlags, const Vector2D &vPoint)
{
SetToolCursor();
return true;
}
//-----------------------------------------------------------------------------
// Purpose: Sets the cursor to the correct cursor for this tool.
//-----------------------------------------------------------------------------
void CToolPickAngles::SetToolCursor(void)
{
static HCURSOR hcur = NULL;
if (!hcur)
{
hcur = LoadCursor(AfxGetInstanceHandle(), MAKEINTRESOURCE(IDC_CROSSHAIR));
}
SetCursor(hcur);
}

77
hammer/ToolPickAngles.h Normal file
View File

@@ -0,0 +1,77 @@
//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============//
//
// Purpose: Tool used for point-and-click picking of angles for filling out
// entity properties.
//
//=============================================================================//
#ifndef TOOLPICKANGLES_H
#define TOOLPICKANGLES_H
#ifdef _WIN32
#pragma once
#endif
#include "MapEntity.h"
#include "ToolInterface.h"
class CMapView3D;
class CToolPickAngles;
//
// Interface for notification by the angles picking tool. Inherit from this if you
// are a client of the angles picker.
//
class IPickAnglesTarget
{
public:
virtual void OnNotifyPickAngles(const Vector &vecPos) = 0;
};
class CToolPickAngles : public CBaseTool
{
public:
//
// Constructor/Destructor
//
CToolPickAngles();
~CToolPickAngles();
//
// CBaseTool virtual implementations
//
virtual ToolID_t GetToolID(void) { return TOOL_PICK_ANGLES; }
virtual bool OnLMouseUp3D(CMapView3D *pView, UINT nFlags, const Vector2D &vPoint);
virtual bool OnLMouseDown3D(CMapView3D *pView, UINT nFlags, const Vector2D &vPoint);
virtual bool OnLMouseDblClk3D(CMapView3D *pView, UINT nFlags, const Vector2D &vPoint);
virtual bool OnRMouseUp3D(CMapView3D *pView, UINT nFlags, const Vector2D &vPoint);
virtual bool OnRMouseDown3D(CMapView3D *pView, UINT nFlags, const Vector2D &vPoint);
virtual bool OnMouseMove3D(CMapView3D *pView, UINT nFlags, const Vector2D &vPoint);
//
// Functions specific to this tool.
//
inline void Attach(IPickAnglesTarget *pTarget);
protected:
void SetToolCursor(void);
IPickAnglesTarget *m_pNotifyTarget; // Object to notify when selection events occur.
};
//-----------------------------------------------------------------------------
// Purpose: Attaches the given notification target to this tool. That object
// will be used for all future notifications and updates by the tool.
//-----------------------------------------------------------------------------
void CToolPickAngles::Attach(IPickAnglesTarget *pNotifyTarget)
{
m_pNotifyTarget = pNotifyTarget;
}
#endif // TOOLPICKANGLES_H

470
hammer/ToolPickEntity.cpp Normal file
View File

@@ -0,0 +1,470 @@
//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============//
//
// Purpose: Tool used for picking entities. This is used anywhere a field that
// contains an entity name is found.
//
// Left click - single select entity
// +Ctrl - multiselect a single entity
//
//=============================================================================//
#include "stdafx.h"
#include "resource.h"
#include "ToolPickEntity.h"
#include "MapViewLogical.h"
#include "MapView3D.h"
// memdbgon must be the last include file in a .cpp file!!!
#include <tier0/memdbgon.h>
//-----------------------------------------------------------------------------
// Purpose: Constructor. Inits data members.
//-----------------------------------------------------------------------------
CToolPickEntity::CToolPickEntity(void)
{
m_pNotifyTarget = NULL;
m_bAllowMultiSelect = false;
}
//-----------------------------------------------------------------------------
// Purpose: Destructor.
//-----------------------------------------------------------------------------
CToolPickEntity::~CToolPickEntity(void)
{
}
//-----------------------------------------------------------------------------
// Purpose: Enables or disables multiselect for this tool.
//-----------------------------------------------------------------------------
void CToolPickEntity::AllowMultiSelect(bool bAllow)
{
m_bAllowMultiSelect = bAllow;
//
// Shouldn't ever happen, but you never know.
//
if ((!bAllow) && (m_Entities.Count() > 1))
{
CMapEntity *pEntity = m_Entities[0].pEntity;
DeselectAll();
SelectEntity(pEntity);
}
}
//-----------------------------------------------------------------------------
// Purpose: Called when the tool is deactivated.
// Input : eNewTool - The ID of the tool that is being activated.
//-----------------------------------------------------------------------------
void CToolPickEntity::OnDeactivate()
{
DeselectAll();
}
//-----------------------------------------------------------------------------
// Purpose: Handles the left mouse button up message in the 3D view.
// Input : pView - The view that the event occurred in.
// nFlags - Flags per the Windows mouse message.
// point - Point in client coordinates where the event occurred.
// Output : Returns true if the message was handled by the tool, false if not.
//-----------------------------------------------------------------------------
bool CToolPickEntity::OnLMouseUp3D(CMapView3D *pView, UINT nFlags, const Vector2D &vPoint)
{
return true;
}
//-----------------------------------------------------------------------------
// Purpose: Handles the left mouse button up message in the 3D view.
// Input : pView - The view that the event occurred in.
// nFlags - Flags per the Windows mouse message.
// point - Point in client coordinates where the event occurred.
// Output : Returns true if the message was handled by the tool, false if not.
//-----------------------------------------------------------------------------
bool CToolPickEntity::OnLMouseDown3D(CMapView3D *pView, UINT nFlags, const Vector2D &vPoint)
{
bool bControl = ((nFlags & MK_CONTROL) != 0);
unsigned long uEntity;
CMapClass *pObject = pView->NearestObjectAt( vPoint, uEntity);
if (pObject != NULL)
{
CMapClass *pSelObject = pObject->PrepareSelection(selectObjects);
CMapEntity *pEntity = dynamic_cast <CMapEntity *>(pSelObject);
if (pEntity != NULL)
{
//
// We clicked on an entity.
//
if ((!m_bAllowMultiSelect) || (!bControl))
{
// Single select.
DeselectAll();
SelectEntity(pEntity);
}
else
{
// Multiselect.
CycleSelectEntity(pEntity);
}
if (m_pNotifyTarget)
{
m_pNotifyTarget->OnNotifyPickEntity(this);
}
}
}
return true;
}
//-----------------------------------------------------------------------------
// Purpose: Handles the left mouse button up message in the 3D view.
// Input : pView - The view that the event occurred in.
// nFlags - Flags per the Windows mouse message.
// point - Point in client coordinates where the event occurred.
// Output : Returns true if the message was handled by the tool, false if not.
//-----------------------------------------------------------------------------
bool CToolPickEntity::OnLMouseDownLogical(CMapViewLogical *pView, UINT nFlags, const Vector2D &vPoint)
{
bool bControl = ((nFlags & MK_CONTROL) != 0);
HitInfo_t hitData;
int nHits = pView->ObjectsAt( vPoint, &hitData, 1 );
if ( ( nHits > 0 ) && hitData.pObject )
{
CMapClass *pSelObject = hitData.pObject->PrepareSelection( selectObjects );
CMapEntity *pEntity = dynamic_cast <CMapEntity *>( pSelObject );
if (pEntity != NULL)
{
//
// We clicked on an entity.
//
if ((!m_bAllowMultiSelect) || (!bControl))
{
// Single select.
DeselectAll();
SelectEntity(pEntity);
}
else
{
// Multiselect.
CycleSelectEntity(pEntity);
}
if (m_pNotifyTarget)
{
m_pNotifyTarget->OnNotifyPickEntity(this);
}
}
}
return true;
}
//-----------------------------------------------------------------------------
// Purpose: Handles the left mouse button double click message in the 3D view.
// Input : pView - The view that the event occurred in.
// nFlags - Flags per the Windows mouse message.
// point - Point in client coordinates where the event occurred.
// Output : Returns true if the message was handled by the tool, false if not.
//-----------------------------------------------------------------------------
bool CToolPickEntity::OnLMouseDblClk3D(CMapView3D *pView, UINT nFlags, const Vector2D &vPoint)
{
return true;
}
//-----------------------------------------------------------------------------
// Purpose: Handles the right mouse button up message in the 3D view.
// Input : pView - The view that the event occurred in.
// nFlags - Flags per the Windows mouse message.
// point - Point in client coordinates where the event occurred.
// Output : Returns true if the message was handled by the tool, false if not.
//-----------------------------------------------------------------------------
bool CToolPickEntity::OnRMouseUp3D(CMapView3D *pView, UINT nFlags, const Vector2D &vPoint)
{
return true;
}
//-----------------------------------------------------------------------------
// Purpose: Handles the mouse button up message in the 3D view.
// Input : pView - The view that the event occurred in.
// nFlags - Flags per the Windows mouse message.
// point - Point in client coordinates where the event occurred.
// Output : Returns true if the message was handled by the tool, false if not.
//-----------------------------------------------------------------------------
bool CToolPickEntity::OnRMouseDown3D(CMapView3D *pView, UINT nFlags, const Vector2D &vPoint)
{
return true;
}
//-----------------------------------------------------------------------------
// Purpose: Handles the mouse move message in the 3D view.
// Input : pView - The view that the event occurred in.
// nFlags - Flags per the Windows mouse message.
// point - Point in client coordinates where the event occurred.
// Output : Returns true if the message was handled by the tool, false if not.
//-----------------------------------------------------------------------------
bool CToolPickEntity::OnMouseMove3D(CMapView3D *pView, UINT nFlags, const Vector2D &vPoint)
{
SetEyedropperCursor();
return true;
}
bool CToolPickEntity::OnMouseMoveLogical(CMapViewLogical *pView, UINT nFlags, const Vector2D &vPoint)
{
SetEyedropperCursor();
return true;
}
//-----------------------------------------------------------------------------
// Purpose: Sets the cursor to the eyedropper cursor.
//-----------------------------------------------------------------------------
void CToolPickEntity::SetEyedropperCursor(void)
{
static HCURSOR hcurEyedropper = NULL;
if (!hcurEyedropper)
{
hcurEyedropper = LoadCursor(AfxGetInstanceHandle(), MAKEINTRESOURCE(IDC_EYEDROPPER));
}
SetCursor(hcurEyedropper);
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : pEntity -
//-----------------------------------------------------------------------------
void CToolPickEntity::CycleSelectEntity(CMapEntity *pEntity)
{
int nIndex = FindEntity(pEntity);
if (nIndex != -1)
{
//
// The entity is in our list, update its selection state.
//
if (m_Entities[nIndex].eState == EntityState_Partial)
{
DeselectEntity(nIndex);
}
else if (m_Entities[nIndex].eState == EntityState_Select)
{
if (m_Entities[nIndex].eOriginalState == EntityState_Partial)
{
m_Entities[nIndex].eState = EntityState_Partial;
//pEntity->SetSelectionState(SELECT_MULTI_PARTIAL);
}
else
{
DeselectEntity(nIndex);
}
}
else if (m_Entities[nIndex].eState == EntityState_None)
{
m_Entities[nIndex].eState = EntityState_Select;
//pEntity->SetSelectionState(SELECT_NORMAL);
}
}
else
{
//
// The entity is not in our list, add it.
//
AddToList(pEntity, EntityState_Select);
//pEntity->SetSelectionState(SELECT_NORMAL);
}
}
//-----------------------------------------------------------------------------
// Purpose: Sets the fully selected and partially selected entities for the picker.
// Input : EntityListFull -
// EntityListPartial -
//-----------------------------------------------------------------------------
void CToolPickEntity::SetSelectedEntities(CMapEntityList &EntityListFull, CMapEntityList &EntityListPartial)
{
m_Entities.RemoveAll();
for (int i = 0; i < EntityListFull.Count(); i++)
{
CMapEntity *pEntity = EntityListFull.Element(i);
AddToList(pEntity, EntityState_Select);
pEntity->SetSelectionState(SELECT_NORMAL);
}
for (int i = 0; i < EntityListPartial.Count(); i++)
{
CMapEntity *pEntity = EntityListPartial.Element(i);
AddToList(pEntity, EntityState_Partial);
pEntity->SetSelectionState(SELECT_MULTI_PARTIAL);
}
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : pEntity -
//-----------------------------------------------------------------------------
int CToolPickEntity::FindEntity(CMapEntity *pEntity)
{
for (int i = 0; i < m_Entities.Count(); i++)
{
if (m_Entities[i].pEntity == pEntity)
{
return i;
}
}
return -1;
}
//-----------------------------------------------------------------------------
// Purpose: Clears the selection set.
//-----------------------------------------------------------------------------
void CToolPickEntity::DeselectAll(void)
{
for (int i = 0; i < m_Entities.Count(); i++)
{
m_Entities[i].pEntity->SetSelectionState(SELECT_NONE);
}
m_Entities.RemoveAll();
}
//-----------------------------------------------------------------------------
// Purpose: Selects the entity unconditionally.
// Input : pEntity - entity to select.
//-----------------------------------------------------------------------------
void CToolPickEntity::SelectEntity(CMapEntity *pEntity)
{
int nIndex = FindEntity(pEntity);
if (nIndex != -1)
{
//
// The entity is in our list, update its selection state.
//
m_Entities[nIndex].eState = EntityState_Select;
pEntity->SetSelectionState(SELECT_NORMAL);
}
else
{
//
// The entity is not in our list, add it.
//
AddToList(pEntity, EntityState_Select);
pEntity->SetSelectionState(SELECT_NORMAL);
}
}
//-----------------------------------------------------------------------------
// Purpose: Deselects the given entity.
//-----------------------------------------------------------------------------
void CToolPickEntity::DeselectEntity(CMapEntity *pEntity)
{
int nIndex = FindEntity(pEntity);
if (nIndex != -1)
{
DeselectEntity(nIndex);
}
}
//-----------------------------------------------------------------------------
// Purpose: Removes the entity at the given index from the selection set.
// Input : nIndex - Index of the entity in the selection list.
//-----------------------------------------------------------------------------
void CToolPickEntity::DeselectEntity(int nIndex)
{
Assert(m_Entities.IsValidIndex(nIndex));
if (m_Entities.IsValidIndex(nIndex))
{
if (m_Entities[nIndex].eOriginalState != EntityState_Partial)
{
m_Entities[nIndex].pEntity->SetSelectionState(SELECT_NONE);
//
// Remove the entity from our list.
//
RemoveFromList(nIndex);
}
else
{
//
// Just set the state to deselected so we can cycle back to partial selection.
//
m_Entities[nIndex].pEntity->SetSelectionState(SELECT_NONE);
m_Entities[nIndex].eState = EntityState_None;
}
}
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : pEntity -
// eState -
//-----------------------------------------------------------------------------
void CToolPickEntity::AddToList(CMapEntity *pEntity, EntityState_t eState)
{
int nIndex = m_Entities.AddToTail();
m_Entities[nIndex].pEntity = pEntity;
m_Entities[nIndex].eState = eState;
m_Entities[nIndex].eOriginalState = eState;
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : nIndex -
//-----------------------------------------------------------------------------
void CToolPickEntity::RemoveFromList(int nIndex)
{
Assert(m_Entities.IsValidIndex(nIndex));
if (m_Entities.IsValidIndex(nIndex))
{
m_Entities.FastRemove(nIndex);
}
}
//-----------------------------------------------------------------------------
// Purpose: Returns lists of fully selected and partially selected entities.
//-----------------------------------------------------------------------------
void CToolPickEntity::GetSelectedEntities(CMapEntityList &EntityListFull, CMapEntityList &EntityListPartial)
{
EntityListFull.RemoveAll();
EntityListPartial.RemoveAll();
for (int i = 0; i < m_Entities.Count(); i++)
{
if (m_Entities[i].eState == EntityState_Select)
{
EntityListFull.AddToTail(m_Entities[i].pEntity);
}
else if (m_Entities[i].eState == EntityState_Partial)
{
EntityListPartial.AddToTail(m_Entities[i].pEntity);
}
}
}

127
hammer/ToolPickEntity.h Normal file
View File

@@ -0,0 +1,127 @@
//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============//
//
// Purpose: Tool used for picking entities for filling out entity properties and
// I/O connections. Anywhere you see an entity name field there should
// be an eyedropper button next to it for picking the entity.
//
// TODO: make the entity field / picker button a single control?
// TODO: combine the face picker with the entity picker?
//
//=============================================================================//
#ifndef TOOLPICKENTITY_H
#define TOOLPICKENTITY_H
#ifdef _WIN32
#pragma once
#endif
#include "MapEntity.h"
#include "ToolInterface.h"
class CMapView3D;
class CMapViewLogical;
class CToolPickEntity;
//
// Selection states for entries in our list of selected faces.
//
enum EntityState_t
{
EntityState_Select = 0, //
EntityState_Partial, // Used for multiselect; the face is in at least one of the face lists being edited.
EntityState_None, // Used for multiselect; to deselect partially selected faces. Otherwise they are removed from the list.
};
//
// An entry in our list of selected entities.
//
struct SelectedEntity_t
{
CMapEntity *pEntity; // Pointer to the entity.
EntityState_t eState; // The current selection state of this entity.
EntityState_t eOriginalState; // The original selection state of this entity.
};
//
// Interface for notification by the entity picking tool. Inherit from this if you
// are a client of the entity picker.
//
class IPickEntityTarget
{
public:
virtual void OnNotifyPickEntity(CToolPickEntity *pTool) = 0;
};
class CToolPickEntity : public CBaseTool
{
public:
//
// Constructor/Destructor
//
CToolPickEntity();
~CToolPickEntity();
//
// CBaseTool virtual implementations
//
virtual void OnDeactivate();
virtual ToolID_t GetToolID(void) { return TOOL_PICK_ENTITY; }
virtual bool OnLMouseUp3D(CMapView3D *pView, UINT nFlags, const Vector2D &vPoint);
virtual bool OnLMouseDown3D(CMapView3D *pView, UINT nFlags, const Vector2D &vPoint);
virtual bool OnLMouseDblClk3D(CMapView3D *pView, UINT nFlags, const Vector2D &vPoint);
virtual bool OnRMouseUp3D(CMapView3D *pView, UINT nFlags, const Vector2D &vPoint);
virtual bool OnRMouseDown3D(CMapView3D *pView, UINT nFlags, const Vector2D &vPoint);
virtual bool OnMouseMove3D(CMapView3D *pView, UINT nFlags, const Vector2D &vPoint);
virtual bool OnLMouseUpLogical(CMapViewLogical *pView, UINT nFlags, const Vector2D &vPoint) { return true; }
virtual bool OnLMouseDownLogical(CMapViewLogical *pView, UINT nFlags, const Vector2D &vPoint);
virtual bool OnLMouseDblClkLogical(CMapViewLogical *pView, UINT nFlags, const Vector2D &vPoint) { return true; }
virtual bool OnRMouseUpLogical(CMapViewLogical *pView, UINT nFlags, const Vector2D &vPoint) { return true; }
virtual bool OnRMouseDownLogical(CMapViewLogical *pView, UINT nFlags, const Vector2D &vPoint) { return true; }
virtual bool OnMouseMoveLogical(CMapViewLogical *pView, UINT nFlags, const Vector2D &vPoint);
//
// Functions specific to this tool.
//
inline void Attach(IPickEntityTarget *pTarget);
void AllowMultiSelect(bool bAllow);
void GetSelectedEntities(CMapEntityList &EntityListFull, CMapEntityList &EntityListPartial);
void SetSelectedEntities(CMapEntityList &EntityListFull, CMapEntityList &EntityListPartial);
protected:
void CycleSelectEntity(CMapEntity *pEntity);
void DeselectAll(void);
void DeselectEntity(int nIndex);
void DeselectEntity(CMapEntity *pEntity);
int FindEntity(CMapEntity *pEntity);
void SelectEntity(CMapEntity *pEntity);
void AddToList(CMapEntity *pEntity, EntityState_t eState);
void RemoveFromList(int nIndex);
void SetEyedropperCursor(void);
IPickEntityTarget *m_pNotifyTarget; // Object to notify when selection events occur.
bool m_bAllowMultiSelect; // If false, only one entity can be selected at a time.
CUtlVector <SelectedEntity_t> m_Entities; // Picked entities and their selection state (partial or full).
};
//-----------------------------------------------------------------------------
// Purpose: Attaches the given notification target to this tool. That object
// will be used for all future notifications and updates by the tool.
//-----------------------------------------------------------------------------
void CToolPickEntity::Attach(IPickEntityTarget *pNotifyTarget)
{
m_pNotifyTarget = pNotifyTarget;
}
#endif // TOOLPICKENTITY_H

269
hammer/ToolPointHandle.cpp Normal file
View File

@@ -0,0 +1,269 @@
//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
//=============================================================================//
#include "stdafx.h"
#include "History.h"
#include "MainFrm.h" // FIXME: For ObjectProperties
#include "MapDoc.h"
#include "MapView2D.h"
#include "MapPointHandle.h"
#include "PopupMenus.h"
#include "Render2D.h"
#include "StatusBarIDs.h" // For SetStatusText
#include "ToolManager.h"
#include "ToolPointHandle.h"
#include "Selection.h"
// memdbgon must be the last include file in a .cpp file!!!
#include <tier0/memdbgon.h>
class CToolPointHandleMsgWnd : public CWnd
{
public:
bool Create(void);
void PreMenu2D(CToolPointHandle *pTool, CMapView2D *pView);
protected:
//{{AFX_MSG_MAP(CToolPointHandleMsgWnd)
afx_msg void OnCenter();
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
private:
CToolPointHandle *m_pToolPointHandle;
CMapView2D *m_pView2D;
};
static CToolPointHandleMsgWnd s_wndToolMessage;
static const char *g_pszClassName = "ValveEditor_PointHandleToolWnd";
BEGIN_MESSAGE_MAP(CToolPointHandleMsgWnd, CWnd)
//{{AFX_MSG_MAP(CToolPointHandleMsgWnd)
ON_COMMAND(ID_CENTER_ON_ENTITY, OnCenter)
//}}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 CToolPointHandleMsgWnd::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 tool to this window before activating the context menu.
//-----------------------------------------------------------------------------
void CToolPointHandleMsgWnd::PreMenu2D(CToolPointHandle *pToolPointHandle, CMapView2D *pView)
{
Assert(pToolPointHandle != NULL);
m_pToolPointHandle = pToolPointHandle;
m_pView2D = pView;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CToolPointHandleMsgWnd::OnCenter()
{
if (m_pToolPointHandle)
{
m_pToolPointHandle->CenterOnParent(m_pView2D);
}
}
//-----------------------------------------------------------------------------
// Purpose: Constructor.
//-----------------------------------------------------------------------------
CToolPointHandle::CToolPointHandle(void)
{
m_pPoint = NULL;
}
//-----------------------------------------------------------------------------
// Purpose: Attaches the point to the tool for manipulation.
//-----------------------------------------------------------------------------
void CToolPointHandle::Attach(CMapPointHandle *pPoint)
{
m_pPoint = pPoint;
}
//-----------------------------------------------------------------------------
// Purpose: Handles left button down events in the 2D view.
// Input : Per CWnd::OnLButtonDown.
// Output : Returns true if the message was handled, false if not.
//-----------------------------------------------------------------------------
bool CToolPointHandle::OnLMouseDown2D(CMapView2D *pView, UINT nFlags, const Vector2D &VPoint)
{
//
// Activate this tool and start dragging the axis endpoint.
//
ToolManager()->PushTool(TOOL_POINT_HANDLE);
pView->SetCapture();
GetHistory()->MarkUndoPosition( m_pDocument->GetSelection()->GetList(), "Modify Origin");
GetHistory()->Keep(m_pPoint);
return true;
}
//-----------------------------------------------------------------------------
// Purpose: Handles left button up events in the 2D view.
// Input : Per CWnd::OnLButtonUp.
// Output : Returns true if the message was handled, false if not.
//-----------------------------------------------------------------------------
bool CToolPointHandle::OnLMouseUp2D(CMapView2D *pView, UINT nFlags, const Vector2D &VPoint)
{
m_pPoint->UpdateOrigin(m_pPoint->m_Origin);
ToolManager()->PopTool();
ReleaseCapture();
m_pDocument->SetModifiedFlag();
m_pDocument->UpdateAllViews( MAPVIEW_UPDATE_TOOL );
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 CToolPointHandle::OnMouseMove2D(CMapView2D *pView, UINT nFlags, const Vector2D &vPoint)
{
//
// Make sure the point is visible.
//
pView->ToolScrollToPoint( vPoint );
//
// Snap the point to half the grid size. Do this so that we can always center
// the origin even on odd-width objects.
//
Vector vecWorld;
pView->ClientToWorld(vecWorld, vPoint);
m_pDocument->Snap(vecWorld, constrainHalfSnap);
//
// Move to the snapped position.
//
m_pPoint->m_Origin[pView->axHorz] = vecWorld[pView->axHorz];
m_pPoint->m_Origin[pView->axVert] = vecWorld[pView->axVert];
//
// Update the status bar and the views.
//
char szBuf[128];
sprintf(szBuf, " @%.0f, %.0f ", m_pPoint->m_Origin[pView->axHorz], m_pPoint->m_Origin[pView->axVert]);
SetStatusText(SBI_COORDS, szBuf);
m_pDocument->UpdateAllViews( MAPVIEW_UPDATE_TOOL );
return true;
}
//-----------------------------------------------------------------------------
// Purpose: Renders the tool in the 2D view.
// Input : pRender - The interface to use for rendering.
//-----------------------------------------------------------------------------
void CToolPointHandle::RenderTool2D(CRender2D *pRender)
{
SelectionState_t eState = m_pPoint->SetSelectionState(SELECT_MODIFY);
m_pPoint->Render2D(pRender);
m_pPoint->SetSelectionState(eState);
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : *pView -
// point -
// Output : Returns true on success, false on failure.
//-----------------------------------------------------------------------------
bool CToolPointHandle::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, IDM_POPUP_POINT_HANDLE));
// Create the window that handles menu messages.
s_wndToolMessage.Create();
}
if (!pView->PointInClientRect(vPoint) )
{
return 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:
//-----------------------------------------------------------------------------
void CToolPointHandle::CenterOnParent(CMapView *pView)
{
if (m_pPoint)
{
GetHistory()->MarkUndoPosition(m_pDocument->GetSelection()->GetList(), "Center Origin");
GetHistory()->Keep(m_pPoint);
CMapClass *pParent = m_pPoint->GetParent();
Vector vecCenter;
pParent->GetBoundsCenter(vecCenter);
m_pPoint->UpdateOrigin(vecCenter);
m_pDocument->SetModifiedFlag();
m_pDocument->UpdateAllViews( MAPVIEW_UPDATE_TOOL );
}
}

49
hammer/ToolPointHandle.h Normal file
View File

@@ -0,0 +1,49 @@
//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//=============================================================================//
#ifndef TOOLPOINTHANDLE_H
#define TOOLPOINTHANDLE_H
#ifdef _WIN32
#pragma once
#endif
#include "ToolInterface.h"
class CMapPointHandle;
class CToolPointHandle : public CBaseTool
{
public:
CToolPointHandle(void);
void Attach(CMapPointHandle *pPoint);
void CenterOnParent(CMapView *pView);
//
// CBaseTool implementation.
//
virtual ToolID_t GetToolID(void) { return TOOL_POINT_HANDLE; }
virtual bool OnLMouseDown2D(CMapView2D *pView, UINT nFlags, const Vector2D &vPoint);
virtual bool OnLMouseUp2D(CMapView2D *pView, UINT nFlags, const Vector2D &vPoint);
virtual bool OnMouseMove2D(CMapView2D *pView, UINT nFlags, const Vector2D &vPoint);
virtual bool OnContextMenu2D(CMapView2D *pView, UINT nFlags, const Vector2D &vPoint);
virtual void RenderTool2D(CRender2D *pRender);
//virtual void RenderTool3D(CRender3D *pRender);
private:
CMapPointHandle *m_pPoint;
};
#endif // TOOLPOINTHANDLE_H

1987
hammer/ToolSelection.cpp Normal file

File diff suppressed because it is too large Load Diff

162
hammer/ToolSelection.h Normal file
View File

@@ -0,0 +1,162 @@
//===== Copyright © 1996-2005, Valve Corporation, All rights reserved. ======//
//
// Purpose:
//
// $NoKeywords: $
//===========================================================================//
#ifndef SELECTION3D_H
#define SELECTION3D_H
#ifdef _WIN32
#pragma once
#endif
#include "Box3D.h"
#include "MapClass.h" // For CMapObjectList
#include "ToolInterface.h"
#include "UtlVector.h"
class CMapWorld;
class CMapView;
class CMapView2D;
class CMapView3D;
class GDinputvariable;
class CRender2D;
class Selection3D : public Box3D
{
public:
Selection3D();
~Selection3D();
void Init( CMapDoc *pDocument );
inline bool IsBoxSelecting();
inline bool IsLogicalBoxSelecting();
void EndBoxSelection();
// Start, end logical selection
void StartLogicalBoxSelection( CMapViewLogical *pView, const Vector &vStart );
void EndLogicalBoxSelection( );
// Tool3D implementation.
virtual void SetEmpty();
virtual bool IsEmpty();
//
// CBaseTool implementation.
//
virtual void OnActivate();
virtual void OnDeactivate();
virtual ToolID_t GetToolID() { return TOOL_POINTER; }
virtual bool OnContextMenu2D(CMapView2D *pView, UINT nFlags, const Vector2D &vPoint);
virtual bool OnKeyDown2D(CMapView2D *pView, UINT nChar, UINT nRepCnt, UINT nFlags);
virtual bool OnLMouseDown2D(CMapView2D *pView, UINT nFlags, const Vector2D &vPoint);
virtual bool OnLMouseUp2D(CMapView2D *pView, UINT nFlags, const Vector2D &vPoint);
virtual bool OnMouseMove2D(CMapView2D *pView, UINT nFlags, const Vector2D &vPoint);
virtual bool OnKeyDown3D(CMapView3D *pView, UINT nChar, UINT nRepCnt, UINT nFlags);
virtual bool OnLMouseDblClk3D(CMapView3D *pView, UINT nFlags, const Vector2D &vPoint);
virtual bool OnLMouseDown3D(CMapView3D *pView, UINT nFlags, const Vector2D &vPoint);
virtual bool OnLMouseUp3D(CMapView3D *pView, UINT nFlags, const Vector2D &vPoint);
virtual bool OnMouseMove3D(CMapView3D *pView, UINT nFlags, const Vector2D &vPoint);
virtual void RenderTool2D(CRender2D *pRender);
virtual void RenderToolLogical(CRender2D *pRender);
virtual void RenderTool3D(CRender3D *pRender);
virtual bool OnContextMenuLogical(CMapViewLogical *pView, UINT nFlags, const Vector2D &vPoint);
virtual bool OnKeyDownLogical(CMapViewLogical *pView, UINT nChar, UINT nRepCnt, UINT nFlags);
virtual bool OnLMouseDownLogical(CMapViewLogical *pView, UINT nFlags, const Vector2D &vPoint);
virtual bool OnLMouseUpLogical(CMapViewLogical *pView, UINT nFlags, const Vector2D &vPoint);
virtual bool OnMouseMoveLogical(CMapViewLogical *pView, UINT nFlags, const Vector2D &vPoint);
virtual bool OnLMouseDblClkLogical(CMapViewLogical *pView, UINT nFlags, const Vector2D &vPoint);
void UpdateSelectionBounds();
bool m_bBoxSelection;
protected:
void TransformSelection();
void TransformLogicalSelection( const Vector2D &vecTranslation );
void FinishTranslation(bool bSave, bool bClone );
void StartTranslation(CMapView *pView, const Vector2D &vPoint, const Vector &vHandleOrigin );
bool StartBoxSelection( CMapView *pView, const Vector2D &vPoint, const Vector &vStart);
void UpdateHandleState();
virtual unsigned int GetConstraints(unsigned int nKeyFlags);
void NudgeObjects(CMapView *pView, int nChar, bool bSnap, bool bClone);
GDinputvariable *ChooseEyedropperVar(CMapView *pView, CUtlVector<GDinputvariable *> &VarList);
CMapEntity *FindEntityInTree(CMapClass *pObject);
void SelectInBox(CMapDoc *pDoc, bool bInsideOnly);
CBaseTool *GetToolObject( CMapView2D *pView, const Vector2D &ptScreen, bool bAttach );
CBaseTool *GetToolObjectLogical( CMapViewLogical *pView, const Vector2D &vPoint, bool bAttach );
void SetEyedropperCursor();
void EyedropperPick2D(CMapView2D *pView, const Vector2D &vPoint);
void EyedropperPick3D(CMapView3D *pView, const Vector2D &vPoint);
void EyedropperPick(CMapView *pView, CMapClass *pObject);
void OnEscape(CMapDoc *pDoc);
//
// Tool3D implementation.
//
virtual int HitTest(CMapView *pView, const Vector2D &pt, bool bTestHandles = false);
// Methods related to logical operations
void EyedropperPickLogical( CMapViewLogical *pView, const Vector2D &vPoint );
bool HitTestLogical( CMapView *pView, const Vector2D &ptClient );
void SelectInLogicalBox(CMapDoc *pDoc, bool bInsideOnly);
CSelection *m_pSelection; // the documents selection opject
bool m_bEyedropper; // True if we are holding down the eyedropper hotkey.
bool m_bSelected; // Did we select an object on left button down?
bool m_b3DEditMode; // editing mode in 3D on/off
bool m_bDrawAsSolidBox; // sometimes we want to render the tool bbox solid
// These are fields related to manipulation in logical views
Vector2D m_vLDownLogicalClient; // Logical client pos at which lbutton was pressed.
Vector2D m_vecLogicalSelBoxMins;
Vector2D m_vecLogicalSelBoxMaxs;
bool m_bInLogicalBoxSelection; // Are we doing box selection in the logical mode?
COLORREF m_clrLogicalBox; // The color of the logical box
Vector2D m_vLastLogicalDragPoint; // Last point at which we dragged (world coords)
Vector2D m_vLogicalTranslation;
bool m_bIsLogicalTranslating; // true while translation in logical view
bool m_bLButtonDown;
bool m_bLeftDragged;
};
//-----------------------------------------------------------------------------
// Are we in box selection?
//-----------------------------------------------------------------------------
inline bool Selection3D::IsBoxSelecting()
{
return m_bBoxSelection;
}
inline bool Selection3D::IsLogicalBoxSelecting()
{
return m_bInLogicalBoxSelection;
}
#endif // SELECTION3D_H

119
hammer/ToolSphere.cpp Normal file
View File

@@ -0,0 +1,119 @@
//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//=============================================================================//
#include "stdafx.h"
#include "History.h"
#include "MainFrm.h" // FIXME: For ObjectProperties
#include "MapDoc.h"
#include "MapView2D.h"
#include "MapSphere.h"
#include "StatusBarIDs.h" // For updating status bar text
#include "ToolManager.h"
#include "ToolSphere.h"
#include "Selection.h"
// memdbgon must be the last include file in a .cpp file!!!
#include <tier0/memdbgon.h>
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
CToolSphere::CToolSphere()
{
m_pSphere = NULL;
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : pSphere -
//-----------------------------------------------------------------------------
void CToolSphere::Attach(CMapSphere *pSphere)
{
m_pSphere = pSphere;
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : pView -
// nFlags -
// point -
// Output : Returns true if the message was handled, false otherwise.
//-----------------------------------------------------------------------------
bool CToolSphere::OnLMouseDown2D(CMapView2D *pView, UINT nFlags, const Vector2D &vPoint)
{
//
// Activate this tool and start resizing the sphere.
//
ToolManager()->PushTool(TOOL_SPHERE);
pView->SetCapture();
GetHistory()->MarkUndoPosition(m_pDocument->GetSelection()->GetList(), "Modify Radius");
GetHistory()->Keep(m_pSphere);
return true;
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : pView -
// nFlags -
// point -
// Output : Returns true if the message was handled, false otherwise.
//-----------------------------------------------------------------------------
bool CToolSphere::OnLMouseUp2D(CMapView2D *pView, UINT nFlags, const Vector2D &vPoint)
{
ToolManager()->PopTool();
ReleaseCapture();
m_pDocument->SetModifiedFlag();
return true;
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : pView -
// nFlags -
// point -
// Output : Returns true if the message was handled, false otherwise.
//-----------------------------------------------------------------------------
bool CToolSphere::OnMouseMove2D(CMapView2D *pView, UINT nFlags, const Vector2D &vPoint)
{
// Make sure the point is visible.
pView->ToolScrollToPoint( vPoint );
Vector vecWorld;
pView->ClientToWorld( vecWorld, vPoint );
m_pDocument->Snap( vecWorld, constrainSnap );
//
// Use whichever axis they dragged the most along as the drag axis.
// Calculate the drag distance in client coordinates.
//
float flHorzRadius = fabs((float)vecWorld[pView->axHorz] - m_pSphere->m_Origin[pView->axHorz]);
float flVertRadius = fabs((float)vecWorld[pView->axVert] - m_pSphere->m_Origin[pView->axVert]);
float flRadius = max(flHorzRadius, flVertRadius);
m_pSphere->SetRadius(flRadius);
//
// Update the status bar text with the new value of the radius.
//
char szBuf[128];
sprintf(szBuf, " %s = %g ", m_pSphere->m_szKeyName, (double)m_pSphere->m_flRadius);
SetStatusText(SBI_COORDS, szBuf);
m_pDocument->UpdateAllViews( MAPVIEW_UPDATE_TOOL );
return true;
}

49
hammer/ToolSphere.h Normal file
View File

@@ -0,0 +1,49 @@
//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//=============================================================================//
#ifndef TOOLSPHERE_H
#define TOOLSPHERE_H
#ifdef _WIN32
#pragma once
#endif
#include "ToolInterface.h"
class CMapSphere;
class CToolSphere : public CBaseTool
{
public:
CToolSphere();
void Attach(CMapSphere *pShere);
//
// CBaseTool implementation.
//
virtual ToolID_t GetToolID(void) { return TOOL_SPHERE; }
virtual bool OnLMouseDown2D(CMapView2D *pView, UINT nFlags, const Vector2D &vPoint);
virtual bool OnLMouseUp2D(CMapView2D *pView, UINT nFlags, const Vector2D &vPoint);
virtual bool OnMouseMove2D(CMapView2D *pView, UINT nFlags, const Vector2D &vPoint);
//virtual void RenderTool2D(CRender2D *pRender);
private:
CMapSphere *m_pSphere;
};
#endif // TOOLSPHERE_H

535
hammer/TorusDlg.cpp Normal file
View File

@@ -0,0 +1,535 @@
//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $Workfile: $
// $Date: $
// $NoKeywords: $
//=============================================================================//
#include "stdafx.h"
#include "hammer.h"
#include "hammer_mathlib.h"
#include "TorusDlg.h"
// memdbgon must be the last include file in a .cpp file!!!
#include <tier0/memdbgon.h>
static LPCTSTR pszSection = "Torus";
void MakeArcCenterRadius(float xCenter, float yCenter, float xrad, float yrad, int npoints, float start_ang, float fArc, float points[][2]);
void MakeArc(float x1, float y1, float x2, float y2, int npoints, float start_ang, float fArc, float points[][2]);
CTorusDlg::CTorusDlg(Vector& boxmins, Vector& boxmaxs, CWnd* pParent /*=NULL*/)
: CDialog(CTorusDlg::IDD, pParent)
{
bmins = boxmins;
bmaxs = boxmaxs;
//{{AFX_DATA_INIT(CTorusDlg)
m_iSides = 0;
m_iWallWidth = 0;
m_iAddHeight = 0;
m_fArc = 0.0f;
m_fAngle = 0.0f;
m_fRotationArc = 0.0f;
m_fRotationAngle = 0.0f;
m_iRotationSides = 0;
m_fCrossSectionRadius = 0;
//}}AFX_DATA_INIT
// load up old defaults
CString str;
m_iWallWidth = AfxGetApp()->GetProfileInt(pszSection, "Wall Width", 32);
str = AfxGetApp()->GetProfileString(pszSection, "Arc_", "360");
m_fArc = atof(str);
m_iSides = AfxGetApp()->GetProfileInt(pszSection, "Sides", 16);
str = AfxGetApp()->GetProfileString(pszSection, "Start Angle_", "0");
m_fAngle = atof(str);
str = AfxGetApp()->GetProfileString(pszSection, "Rotation Arc_", "360");
m_fRotationArc = atof(str);
m_iRotationSides = AfxGetApp()->GetProfileInt(pszSection, "Rotation Sides", 16);
str = AfxGetApp()->GetProfileString(pszSection, "Rotation Start Angle_", "0");
m_fRotationAngle = atof(str);
m_iAddHeight = AfxGetApp()->GetProfileInt(pszSection, "Add Height", 0);
Vector vecSize;
VectorSubtract( bmaxs, bmins, vecSize );
if ( m_iAddHeight > vecSize.z )
{
m_iAddHeight = vecSize.z;
}
// This is the maximum cross-section radius
m_fCrossSectionRadius = MaxTorusCrossSectionRadius();
}
void CTorusDlg::SaveValues()
{
CString str;
AfxGetApp()->WriteProfileInt(pszSection, "Wall Width", m_iWallWidth);
str.Format("%f", m_fArc);
AfxGetApp()->WriteProfileString(pszSection, "Arc_", str);
AfxGetApp()->WriteProfileInt(pszSection, "Sides", m_iSides);
str.Format("%f", m_fAngle);
AfxGetApp()->WriteProfileString(pszSection, "Start Angle_", str);
str.Format("%f", m_fRotationArc);
AfxGetApp()->WriteProfileString(pszSection, "Rotation Arc_", str);
str.Format("%f", m_fRotationAngle);
AfxGetApp()->WriteProfileString(pszSection, "Rotation Start Angle_", str);
AfxGetApp()->WriteProfileInt(pszSection, "Rotation Sides", m_iRotationSides);
AfxGetApp()->WriteProfileInt(pszSection, "Add Height", m_iAddHeight);
}
void CTorusDlg::DoDataExchange(CDataExchange* pDX)
{
CDialog::DoDataExchange(pDX);
//{{AFX_DATA_MAP(CTorusDlg)
DDX_Control(pDX, IDC_ANGLESPIN, m_cStartAngleSpin);
DDX_Control(pDX, IDC_WALLWIDTHSPIN, m_cWallWidthSpin);
DDX_Control(pDX, IDC_WALLWIDTH, m_cWallWidth);
DDX_Control(pDX, IDC_SIDESSPIN, m_cSidesSpin);
DDX_Control(pDX, IDC_SIDES, m_cSides);
DDX_Control(pDX, IDC_ARCSPIN, m_cArcSpin);
DDX_Control(pDX, IDC_ARC, m_cArc);
DDX_Control(pDX, IDC_PREVIEW, m_cPreview);
DDX_Control(pDX, IDC_PREVIEW_TOP_VIEW, m_cTopViewPreview);
DDX_Text(pDX, IDC_SIDES, m_iSides);
DDV_MinMaxInt(pDX, m_iSides, 3, 2048);
DDX_Text(pDX, IDC_WALLWIDTH, m_iWallWidth);
DDX_Text(pDX, IDC_ADDHEIGHT, m_iAddHeight);
DDX_Text(pDX, IDC_ARC, m_fArc);
DDV_MinMaxFloat(pDX, m_fArc, 8.f, 360.f);
DDX_Text(pDX, IDC_ANGLE, m_fAngle);
DDV_MinMaxFloat(pDX, m_fAngle, -360.0f, 360.f);
DDX_Text(pDX, IDC_ROTATION_ARC, m_fRotationArc);
DDV_MinMaxFloat(pDX, m_fRotationArc, 0.f, 3600.f);
DDX_Text(pDX, IDC_ROTATION_ANGLE, m_fRotationAngle);
DDV_MinMaxFloat(pDX, m_fRotationAngle, -360.0f, 360.f);
DDX_Text(pDX, IDC_ROTATION_SIDES, m_iRotationSides);
DDV_MinMaxInt(pDX, m_iRotationSides, 3, 2048);
DDX_Text(pDX, IDC_CROSS_SECTION_RADIUS, m_fCrossSectionRadius);
DDV_MinMaxFloat(pDX, m_fCrossSectionRadius, 0.f, 5000.f);
//}}AFX_DATA_MAP
if ( pDX->m_bSaveAndValidate )
{
Vector vecSize;
VectorSubtract( bmaxs, bmins, vecSize );
if ( m_iAddHeight > vecSize.z )
{
m_iAddHeight = vecSize.z;
}
}
}
BEGIN_MESSAGE_MAP(CTorusDlg, CDialog)
//{{AFX_MSG_MAP(CTorusDlg)
ON_EN_CHANGE(IDC_ARC, OnChangeArc)
ON_EN_CHANGE(IDC_ROTATION_ARC, OnChangeTorusArc)
ON_BN_CLICKED(IDC_CIRCLE, OnCircle)
ON_BN_CLICKED(IDC_TORUS_COMPUTE_RADIUS, OnComputeRadius)
ON_EN_UPDATE(IDC_SIDES, OnUpdateSides)
ON_EN_UPDATE(IDC_WALLWIDTH, OnUpdateWallwidth)
ON_WM_PAINT()
ON_BN_CLICKED(IDC_TORUS_PREVIEW, OnTorusPreview)
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
/////////////////////////////////////////////////////////////////////////////
// CTorusDlg message handlers
void CTorusDlg::OnChangeArc()
{
}
void CTorusDlg::OnChangeTorusArc()
{
}
void CTorusDlg::OnCircle()
{
m_cArcSpin.SetPos(360);
OnTorusPreview();
}
void CTorusDlg::OnComputeRadius()
{
UpdateData( TRUE );
// This is the maximum cross-section radius
m_fCrossSectionRadius = MaxTorusCrossSectionRadius();
UpdateData( FALSE );
OnTorusPreview();
}
void CTorusDlg::OnUpdateSides()
{
}
void CTorusDlg::OnUpdateWallwidth()
{
}
BOOL CTorusDlg::OnInitDialog()
{
CDialog::OnInitDialog();
m_cArcSpin.SetRange(8, 360);
m_cSidesSpin.SetRange(3, 100);
m_cWallWidthSpin.SetRange(2, m_iMaxWallWidth);
m_cStartAngleSpin.SetRange(0, 360);
m_cPreview.ShowWindow(SW_HIDE);
m_cTopViewPreview.ShowWindow(SW_HIDE);
bInitialized = TRUE;
return TRUE;
}
void CTorusDlg::OnPaint()
{
CPaintDC dc(this); // device context for painting
CBrush black(RGB(0,0,0));
CBrush grey(RGB(128,128,128));
// Do not call CDialog::OnPaint() for painting messages
CRect rcPreview;
m_cPreview.GetWindowRect(&rcPreview);
ScreenToClient(&rcPreview);
dc.FillRect(rcPreview, &black);
DrawTorusCrossSection( &dc );
rcPreview.InflateRect(1,1);
dc.FrameRect(rcPreview, &grey);
ValidateRect(rcPreview);
m_cTopViewPreview.GetWindowRect(&rcPreview);
ScreenToClient(&rcPreview);
dc.FillRect(rcPreview, &black);
DrawTorusTopView( &dc );
rcPreview.InflateRect(1,1);
dc.FrameRect(rcPreview, &grey);
ValidateRect(rcPreview);
bInitialized = TRUE;
}
void CTorusDlg::OnTorusPreview()
{
//
// Build preview.
//
bInitialized = TRUE;
InvalidateRect(NULL);
UpdateWindow();
}
CTorusDlg::~CTorusDlg()
{
}
//-----------------------------------------------------------------------------
// Gets the inner radius of the torus cross-section
//-----------------------------------------------------------------------------
float CTorusDlg::MaxTorusCrossSectionRadius() const
{
Vector vecSize;
VectorSubtract( bmaxs, bmins, vecSize );
float flTorusRadius = (vecSize.z - m_iAddHeight);
// Check for multiple revolutions...
if ( ( m_fRotationArc > 360 ) || (( m_fRotationArc == 360 ) && ( m_iAddHeight != 0 )) )
{
// Height per revolution
float flHeightPerRev = 360.0f * m_iAddHeight / m_fRotationArc;
if ( flHeightPerRev < flTorusRadius )
{
flTorusRadius = flHeightPerRev;
}
}
// Also constrain it based on x & y too...
if ( (vecSize.x * 0.5f) < flTorusRadius )
{
flTorusRadius = vecSize.x * 0.5f;
}
if ( (vecSize.y * 0.5f) < flTorusRadius )
{
flTorusRadius = vecSize.y * 0.5f;
}
flTorusRadius *= 0.5f;
if ( flTorusRadius < m_iWallWidth )
{
flTorusRadius = m_iWallWidth;
}
return flTorusRadius;
}
//-----------------------------------------------------------------------------
// Gets the inner radius of the torus cross-section
//-----------------------------------------------------------------------------
float CTorusDlg::GetTorusCrossSectionRadius() const
{
Vector vecSize;
VectorSubtract( bmaxs, bmins, vecSize );
float flTorusRadius = m_fCrossSectionRadius;
// Also constrain it based on x & y too...
if ( (vecSize.x * 0.25f) < flTorusRadius )
{
flTorusRadius = vecSize.x * 0.25f;
}
if ( (vecSize.y * 0.25f) < flTorusRadius )
{
flTorusRadius = vecSize.y * 0.25f;
}
return flTorusRadius;
}
//-----------------------------------------------------------------------------
// Draws the torus cross-section
//-----------------------------------------------------------------------------
void CTorusDlg::DrawTorusCrossSection(CDC* pDC )
{
int iSides, iWallWidth;
float fArc, fStartAngle;
int i;
float fOuterPoints[ARC_MAX_POINTS][2];
float fInnerPoints[ARC_MAX_POINTS][2];
float fScaleX;
float fScaleY;
UpdateData(TRUE);
CPen m_hPen, *pOldPen;
m_hPen.CreatePen(PS_SOLID, 1, RGB(255,255,255));
pOldPen = pDC->SelectObject(&m_hPen);
CRect rcItem;
m_cPreview.GetWindowRect(&rcItem);
ScreenToClient(&rcItem);
CPoint pt;
pt.x = rcItem.left + rcItem.Width() / 2;
pt.y = rcItem.top + rcItem.Height() / 2;
float flMaxRadius = GetTorusCrossSectionRadius();
iWallWidth = m_iWallWidth;
if ( iWallWidth > flMaxRadius )
iWallWidth = flMaxRadius;
float flTorusRadius = flMaxRadius - iWallWidth;
float flDeltaZ = bmaxs[2] - bmins[2];
if (flDeltaZ)
{
if ( flDeltaZ < flMaxRadius * 2.0f )
{
flDeltaZ = flMaxRadius * 2.0f;
}
fScaleX = rcItem.Width()/flDeltaZ;
fScaleY = rcItem.Height()/flDeltaZ;
}
else
{
fScaleX = fScaleY = 1.0f;
// fScaleX = rcItem.Width() / (2.0f * flMaxRadius);
// fScaleY = rcItem.Height() / (2.0f * flMaxRadius);
}
fArc = m_fArc;
fStartAngle = m_fAngle;
iSides = m_iSides;
MakeArcCenterRadius(0, 0,
flTorusRadius + iWallWidth, flTorusRadius + iWallWidth,
iSides, fStartAngle, fArc, fOuterPoints);
MakeArcCenterRadius(0, 0,
flTorusRadius, flTorusRadius,
iSides, fStartAngle, fArc, fInnerPoints);
// check wall width - if it's half or more of the total,
// set the inner poinst to the center point of the box
// and turn off the CreateSouthFace flag
Vector points[4];
for (i = 0; i < iSides; i++)
{
int iNextPoint = i+1;
if (iNextPoint >= iSides + 1)
{
iNextPoint = 0;
}
points[0][0] = fOuterPoints[i][0];
points[0][1] = fOuterPoints[i][1];
points[1][0] = fOuterPoints[iNextPoint][0];
points[1][1] = fOuterPoints[iNextPoint][1];
points[2][0] = fInnerPoints[iNextPoint][0];
points[2][1] = fInnerPoints[iNextPoint][1];
points[3][0] = fInnerPoints[i][0];
points[3][1] = fInnerPoints[i][1];
for (int j = 0; j < 4; j++)
{
points[j][0] = fScaleX * points[j][0];
points[j][1] = fScaleY * points[j][1];
}
pDC->MoveTo(pt.x + (int)points[1][0], pt.y - (int)points[1][1]);
pDC->LineTo(pt.x + (int)points[0][0], pt.y - (int)points[0][1]);
pDC->LineTo(pt.x + (int)points[3][0], pt.y - (int)points[3][1]);
pDC->LineTo(pt.x + (int)points[2][0], pt.y - (int)points[2][1]);
}
// Close the cross-section off...
if ( fArc != 360.0f )
{
pDC->LineTo(pt.x + (int)points[1][0], pt.y - (int)points[1][1]);
}
pDC->SelectObject(pOldPen);
}
void CTorusDlg::DrawTorusTopView( CDC* pDC )
{
int i;
float fOuterPoints[ARC_MAX_POINTS][2];
float fInnerPoints[ARC_MAX_POINTS][2];
float fScaleX;
float fScaleY;
UpdateData(TRUE);
CPen m_hPen, *pOldPen;
m_hPen.CreatePen(PS_SOLID, 1, RGB(255,255,255));
pOldPen = pDC->SelectObject(&m_hPen);
CRect rcItem;
m_cTopViewPreview.GetWindowRect(&rcItem);
ScreenToClient(&rcItem);
CPoint pt;
pt.x = rcItem.left + rcItem.Width() / 2;
pt.y = rcItem.top + rcItem.Height() / 2;
if (bmaxs[0] - bmins[0])
{
fScaleX = rcItem.Width() /(bmaxs[0] - bmins[0]);
}
else
{
fScaleX = 1.0f;
}
if (bmaxs[1] - bmins[1])
{
fScaleY = rcItem.Height() /(bmaxs[1] - bmins[1]);
}
else
{
fScaleY = 1.0f;
}
int iSides, iWallWidth;
float fArc, fStartAngle;
fArc = m_fRotationArc;
fStartAngle = m_fRotationAngle;
iSides = m_iRotationSides;
iWallWidth = GetTorusCrossSectionRadius() * 2.0f;
float xCenter = (bmaxs[0] + bmins[0]) * 0.5f;
float yCenter = (bmaxs[1] + bmins[1]) * 0.5f;
float xRad = (bmaxs[0] - xCenter - iWallWidth);
float yRad = (bmaxs[1] - yCenter - iWallWidth);
if (xRad < 0.0f )
{
xRad = 0.0f;
}
if (yRad < 0.0f )
{
yRad = 0.0f;
}
MakeArcCenterRadius(xCenter, yCenter, xRad + iWallWidth, yRad + iWallWidth,
iSides, fStartAngle, fArc, fOuterPoints);
MakeArcCenterRadius(xCenter, yCenter, xRad, yRad,
iSides, fStartAngle, fArc, fInnerPoints);
Vector vecCenter;
VectorLerp( bmins, bmaxs, 0.5f, vecCenter );
Vector points[4];
for (i = 0; i < iSides; i++)
{
int iNextPoint = i+1;
if (iNextPoint >= iSides + 1)
{
iNextPoint = 0;
}
points[0][0] = fOuterPoints[i][0];
points[0][1] = fOuterPoints[i][1];
points[1][0] = fOuterPoints[iNextPoint][0];
points[1][1] = fOuterPoints[iNextPoint][1];
points[2][0] = fInnerPoints[iNextPoint][0];
points[2][1] = fInnerPoints[iNextPoint][1];
points[3][0] = fInnerPoints[i][0];
points[3][1] = fInnerPoints[i][1];
for (int j = 0; j < 4; j++)
{
points[j][0] = fScaleX * (points[j][0] - vecCenter[0]);
points[j][1] = fScaleY * (points[j][1] - vecCenter[1]);
}
pDC->MoveTo(pt.x + (int)points[1][0], pt.y - (int)points[1][1]);
pDC->LineTo(pt.x + (int)points[0][0], pt.y - (int)points[0][1]);
pDC->LineTo(pt.x + (int)points[3][0], pt.y - (int)points[3][1]);
pDC->LineTo(pt.x + (int)points[2][0], pt.y - (int)points[2][1]);
}
// Close the cross-section off...
if ( fArc != 360.0f )
{
pDC->LineTo(pt.x + (int)points[1][0], pt.y - (int)points[1][1]);
}
pDC->SelectObject(pOldPen);
}

303
hammer/VGuiWnd.cpp Normal file
View File

@@ -0,0 +1,303 @@
//===== Copyright © 1996-2007, Valve Corporation, All rights reserved. ======//
//
// Purpose:
//
// $Workfile: $
// $Date: $
// $NoKeywords: $
//===========================================================================//
#include "stdafx.h"
#include "vguiwnd.h"
#include <vgui_controls/EditablePanel.h>
#include "vgui/ISurface.h"
#include "vgui/IVGui.h"
#include "VGuiMatSurface/IMatSystemSurface.h"
#include "HammerVGui.h"
#include "material.h"
#include "istudiorender.h"
#include "hammer.h"
#include "toolutils/enginetools_int.h"
#include "toolframework/ienginetool.h"
#include "ienginevgui.h"
IMPLEMENT_DYNCREATE(CVGuiPanelWnd, CWnd)
#define REPAINT_TIMER_ID 1042 //random value, hopfully no collisions
class CBaseMainPanel : public vgui::EditablePanel
{
public:
CBaseMainPanel(Panel *parent, const char *panelName) : vgui::EditablePanel( parent, panelName ) {};
virtual void OnSizeChanged(int newWide, int newTall)
{
// call Panel and not EditablePanel OnSizeChanged.
Panel::OnSizeChanged(newWide, newTall);
}
};
LRESULT CVGuiPanelWnd::WindowProc( UINT message, WPARAM wParam, LPARAM lParam )
{
if ( !WindowProcVGui( message, wParam, lParam ) )
{
return CWnd::WindowProc( message, wParam, lParam ) ;
}
return 1;
}
BOOL CVGuiPanelWnd::OnEraseBkgnd(CDC* pDC)
{
return TRUE;
}
BEGIN_MESSAGE_MAP(CVGuiPanelWnd, CWnd)
ON_WM_ERASEBKGND()
END_MESSAGE_MAP()
CVGuiWnd::CVGuiWnd(void)
{
m_pMainPanel = NULL;
m_pParentWnd = NULL;
m_hVGuiContext = vgui::DEFAULT_VGUI_CONTEXT;
m_bIsDrawing = false;
m_ClearColor.SetColor( 0,0,0,255 );
m_bClearZBuffer = true;
}
CVGuiWnd::~CVGuiWnd(void)
{
if ( HammerVGui()->HasFocus( this ) )
{
HammerVGui()->SetFocus( NULL );
}
if ( m_hVGuiContext != vgui::DEFAULT_VGUI_CONTEXT )
{
vgui::ivgui()->DestroyContext( m_hVGuiContext );
m_hVGuiContext = vgui::DEFAULT_VGUI_CONTEXT;
}
// kill the timer if any
::KillTimer( m_pParentWnd->GetSafeHwnd(), REPAINT_TIMER_ID );
if ( m_pMainPanel )
m_pMainPanel->MarkForDeletion();
}
void CVGuiWnd::SetParentWindow(CWnd *pParent)
{
m_pParentWnd = pParent;
m_pParentWnd->EnableWindow( true );
m_pParentWnd->SetFocus();
}
int CVGuiWnd::GetVGuiContext()
{
return m_hVGuiContext;
}
void CVGuiWnd::SetCursor(vgui::HCursor cursor)
{
if ( m_pMainPanel )
{
m_pMainPanel->SetCursor( cursor );
}
}
void CVGuiWnd::SetCursor(const char *filename)
{
vgui::HCursor hCursor = vgui::surface()->CreateCursorFromFile( filename );
m_pMainPanel->SetCursor( hCursor );
}
void CVGuiWnd::SetMainPanel( vgui::EditablePanel * pPanel )
{
Assert( m_pMainPanel == NULL );
Assert( m_hVGuiContext == vgui::DEFAULT_VGUI_CONTEXT );
m_pMainPanel = pPanel;
m_pMainPanel->SetParent( vgui::surface()->GetEmbeddedPanel() );
m_pMainPanel->SetVisible( true );
m_pMainPanel->SetPaintBackgroundEnabled( false );
m_pMainPanel->SetCursor( vgui::dc_arrow );
// Initially, don't trap mouse input in case the engine is around (if we have this set to true and they go to the engine,
// it'll hog mouse input that the engine should get).
m_pMainPanel->SetMouseInputEnabled( false );
m_hVGuiContext = vgui::ivgui()->CreateContext();
vgui::ivgui()->AssociatePanelWithContext( m_hVGuiContext, m_pMainPanel->GetVPanel() );
}
vgui::EditablePanel *CVGuiWnd::CreateDefaultPanel()
{
return new CBaseMainPanel( NULL, "mainpanel" );
}
vgui::EditablePanel *CVGuiWnd::GetMainPanel()
{
return m_pMainPanel;
}
CWnd *CVGuiWnd::GetParentWnd()
{
return m_pParentWnd;
}
void CVGuiWnd::SetRepaintInterval( int msecs )
{
::SetTimer( m_pParentWnd->GetSafeHwnd(), REPAINT_TIMER_ID, msecs, NULL );
}
void CVGuiWnd::DrawVGuiPanel()
{
if ( !m_pMainPanel || !m_pParentWnd || m_bIsDrawing )
return;
m_bIsDrawing = true; // avoid recursion
HWND hWnd = m_pParentWnd->GetSafeHwnd();
int w,h;
RECT rect; ::GetClientRect(hWnd, &rect);
CMatRenderContextPtr pRenderContext( MaterialSystemInterface() );
MaterialSystemInterface()->SetView( hWnd );
pRenderContext->Viewport( 0, 0, rect.right, rect.bottom );
pRenderContext->ClearColor4ub( m_ClearColor.r(), m_ClearColor.g(), m_ClearColor.b(), m_ClearColor.a() );
pRenderContext->ClearBuffers( true, m_bClearZBuffer );
MaterialSystemInterface()->BeginFrame( 0 );
g_pStudioRender->BeginFrame();
// draw from the main panel down
m_pMainPanel->GetSize( w , h );
if ( w != rect.right || h != rect.bottom )
{
m_pMainPanel->SetBounds(0, 0, rect.right, rect.bottom );
m_pMainPanel->Repaint();
}
HammerVGui()->Simulate();
// Don't draw the engine's vgui stuff when in .
int iWasVisible = -1;
vgui::VPANEL hRoot = NULL;
if ( APP()->IsFoundryMode() && enginevgui )
{
hRoot = enginevgui->GetPanel( PANEL_ROOT );
iWasVisible = g_pVGuiPanel->IsVisible( hRoot );
g_pVGuiPanel->SetVisible( hRoot, false );
}
vgui::surface()->RestrictPaintToSinglePanel( m_pMainPanel->GetVPanel(), true );
vgui::surface()->PaintTraverseEx( m_pMainPanel->GetVPanel(), true );
vgui::surface()->RestrictPaintToSinglePanel( NULL );
if ( iWasVisible != -1 )
{
g_pVGuiPanel->SetVisible( hRoot, (iWasVisible != 0) );
}
g_pStudioRender->EndFrame();
MaterialSystemInterface()->EndFrame();
MaterialSystemInterface()->SwapBuffers();
if ( enginetools )
{
MaterialSystemInterface()->SetView( enginetools->GetEngineHwnd() );
}
m_bIsDrawing = false;
}
LRESULT CVGuiWnd::WindowProcVGui( UINT uMsg, WPARAM wParam, LPARAM lParam )
{
switch(uMsg)
{
case WM_GETDLGCODE :
{
// forward all keyboard into to vgui panel
return DLGC_WANTALLKEYS|DLGC_WANTCHARS;
}
case WM_PAINT :
{
// draw the VGUI panel now
DrawVGuiPanel();
break;
}
case WM_TIMER :
{
if ( wParam == REPAINT_TIMER_ID )
{
m_pParentWnd->Invalidate();
}
break;
}
case WM_SETCURSOR:
return 1; // don't pass WM_SETCURSOR
case WM_LBUTTONDOWN:
case WM_RBUTTONDOWN:
case WM_MBUTTONDOWN:
case WM_MOUSEMOVE:
{
// switch vgui focus to this panel
HammerVGui()->SetFocus( this );
// request keyboard focus too on mouse down
if ( uMsg != WM_MOUSEMOVE)
{
m_pParentWnd->Invalidate();
m_pParentWnd->SetFocus();
}
break;
}
case WM_KILLFOCUS:
{
// restore normal arrow cursor when mouse leaves VGUI panel
SetCursor( vgui::dc_arrow );
break;
}
case WM_LBUTTONUP:
case WM_RBUTTONUP:
case WM_MBUTTONUP:
case WM_LBUTTONDBLCLK:
case WM_RBUTTONDBLCLK:
case WM_MBUTTONDBLCLK:
case WM_MOUSEWHEEL:
case WM_KEYDOWN:
case WM_SYSKEYDOWN:
case WM_SYSCHAR:
case WM_CHAR:
case WM_KEYUP:
case WM_SYSKEYUP:
{
// redraw window
if ( m_pParentWnd )
{
m_pParentWnd->Invalidate();
}
break;
}
}
return 0;
}

77
hammer/VGuiWnd.h Normal file
View File

@@ -0,0 +1,77 @@
#pragma once
#include "afxwin.h"
#include "color.h"
namespace vgui
{
class EditablePanel;
typedef unsigned long HCursor;
}
class CVGuiWnd
{
public:
CVGuiWnd(void);
~CVGuiWnd(void);
public:
void SetMainPanel( vgui::EditablePanel * pPanel );
vgui::EditablePanel *GetMainPanel(); // returns VGUI main panel
vgui::EditablePanel *CreateDefaultPanel();
void SetParentWindow(CWnd *pParent);
CWnd *GetParentWnd(); // return CWnd handle
void SetCursor(vgui::HCursor cursor);
void SetCursor(const char *filename);
void SetRepaintInterval( int msecs );
int GetVGuiContext();
// The Hammer 2D views basically ignore vgui input. They're only there to render on top of.
// When we pass true here, CMatSystemSurface::RunFrame ignores all the input events.
// If we pass false (as the model browser does), then it does process input events and send them to the vgui panels.
// Eventually, we could change the 2D views' input events to come from the input system instead of from MFC.
virtual bool IsModal()
{
return false;
}
protected:
void DrawVGuiPanel(); // overridden to draw this view
long WindowProcVGui( UINT message, WPARAM wParam, LPARAM lParam ); //
vgui::EditablePanel *m_pMainPanel;
CWnd *m_pParentWnd;
int m_hVGuiContext;
bool m_bIsDrawing;
Color m_ClearColor;
bool m_bClearZBuffer;
};
class CVGuiPanelWnd: public CWnd, public CVGuiWnd
{
protected:
DECLARE_DYNCREATE(CVGuiPanelWnd)
public:
// Generated message map functions
//{{AFX_MSG(CVGuiViewModel)
BOOL OnEraseBkgnd(CDC* pDC);
//}}AFX_MSG
virtual LRESULT WindowProc( UINT message, WPARAM wParam, LPARAM lParam );
// See CVGuiWnd's function for a description but this basically tells Hammer to actually pass vgui messages to our panel.
virtual bool IsModal()
{
return true;
}
DECLARE_MESSAGE_MAP()
};

File diff suppressed because it is too large Load Diff

281
hammer/activeitemlist.h Normal file
View File

@@ -0,0 +1,281 @@
//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $Workfile: $
// $Date: $
//
//-----------------------------------------------------------------------------
// $Log: $
//
// $NoKeywords: $
//=============================================================================//
#ifndef ACTIVEITEMLIST_H
#define ACTIVEITEMLIST_H
#pragma once
// memdbgon must be the last include file in a .cpp file!!!
#include <tier0/memdbgon.h>
//=============================================================================
typedef int ITEM_HANDLE;
//=============================================================================
template <class T> class ActiveItemList
{
public:
ActiveItemList();
ActiveItemList( int size );
void SetSize( int size );
int GetSize( void );
int GetNumberOfItems( void );
T* GetFirstItem( void );
T* GetNextItem( void );
ITEM_HANDLE GetEmptyItemHandle( void );
T* GetItem( ITEM_HANDLE handle );
void RemoveItem( ITEM_HANDLE handle );
void SetActiveItem( ITEM_HANDLE handle );
T* GetActiveItem( void );
void Free( void );
protected:
int m_NumItems; // the number of items in the list
int m_ActiveItem; // the active item index
int m_CurrentItem;
int m_ListSize; // size of the list
T *m_pList; // the active item list
bool *m_pEmptyList; // keep an empty list
};
//=============================================================================
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
template <class T> ActiveItemList<T>::ActiveItemList()
{
m_NumItems = 0;
m_ActiveItem = -1;
m_CurrentItem = -1;
m_ListSize = 0;
m_pList = NULL;
m_pEmptyList = NULL;
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
template <class T> ActiveItemList<T>::ActiveItemList( int size )
{
int i; // loop counter
// set the size of the list
m_ListSize = size;
//
// allocate memory for the list
//
if( !( m_pList = new T[size] ) )
return;
if( !( m_pEmptyList = new bool[size] ) )
return;
//
// initialize the active item list
//
m_NumItems = 0;
m_ActiveItem = -1;
m_CurrentItem = -1;
for( i = 0; i < size; i++ )
m_pEmptyList[i] = true;
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
template <class T> void ActiveItemList<T>::SetSize( int size )
{
int i; // loop counter
// set the size of the list
m_ListSize = size;
//
// allocate memory for the list
//
if( !( m_pList = new T[size] ) )
return;
if( !( m_pEmptyList = new bool[size] ) )
return;
//
// initialize the active item list
//
m_NumItems = 0;
m_ActiveItem = -1;
m_CurrentItem = -1;
for( i = 0; i < size; i++ )
m_pEmptyList[i] = true;
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
template <class T> int ActiveItemList<T>::GetSize( void )
{
return m_ListSize;
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
template <class T> int ActiveItemList<T>::GetNumberOfItems( void )
{
return m_NumItems;
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
template <class T> T* ActiveItemList<T>::GetFirstItem( void )
{
int i; // loop counter
// reset current item index
m_CurrentItem = -1;
//
// find the first item in the list
//
for( i = 0; i < m_ListSize; i++ )
{
if( !m_pEmptyList[i] )
{
m_CurrentItem = i;
return &m_pList[i];
}
}
// no items found
return NULL;
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
template <class T> T* ActiveItemList<T>::GetNextItem( void )
{
int i; // loop counter
//
// find the next item in the list
//
for( i = m_CurrentItem + 1; i < m_ListSize; i++ )
{
if( !m_pEmptyList[i] )
{
m_CurrentItem = i;
return &m_pList[i];
}
}
// no more items found
return NULL;
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
template <class T> ITEM_HANDLE ActiveItemList<T>::GetEmptyItemHandle( void )
{
int i; // loop counter
//
// find an empty item slot and return the handle
//
for( i = 0; i < m_ListSize; i++ )
{
if( m_pEmptyList[i] )
{
m_pEmptyList[i] = false;
m_NumItems++;
return i;
}
}
// no empty item slot
return -1;
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
template <class T> T* ActiveItemList<T>::GetItem( ITEM_HANDLE handle )
{
return &m_pList[handle];
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
template <class T> void ActiveItemList<T>::RemoveItem( ITEM_HANDLE handle )
{
//
// set the item to empty and decrement the number of items in list
//
m_pEmptyList[handle] = true;
m_NumItems--;
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
template <class T> void ActiveItemList<T>::SetActiveItem( ITEM_HANDLE handle )
{
// set the active item
m_ActiveItem = handle;
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
template <class T> T* ActiveItemList<T>::GetActiveItem( void )
{
if( m_ActiveItem == -1 )
return NULL;
return &m_pList[m_ActiveItem];
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
template <class T> void ActiveItemList<T>::Free( void )
{
//
// clean up lists
//
if( m_pList )
delete [] m_pList;
if( m_pEmptyList )
delete [] m_pEmptyList;
}
#endif // ACTIVEITEMLIST_H

562
hammer/anglebox.cpp Normal file
View File

@@ -0,0 +1,562 @@
//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============//
//
// Purpose: Implements the angle custom control, a circle with a line indicating
// a rotation angle.
//
//=============================================================================//
#include "stdafx.h"
#include "hammer.h"
#include "AngleBox.h"
#include "hammer_mathlib.h"
#include "CustomMessages.h"
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"
#pragma warning(disable: 4244)
BEGIN_MESSAGE_MAP(CAngleBox, CWnd)
//{{AFX_MSG_MAP(CAngleBox)
ON_WM_MOUSEMOVE()
ON_WM_LBUTTONUP()
ON_WM_LBUTTONDOWN()
ON_WM_PAINT()
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
//-----------------------------------------------------------------------------
// Purpose: Constructor.
//-----------------------------------------------------------------------------
CAngleBox::CAngleBox(void)
{
m_vecAngles.Init();
m_bDragging = false;
m_pEdit = NULL;
}
//-----------------------------------------------------------------------------
// Purpose: Destructor.
//-----------------------------------------------------------------------------
CAngleBox::~CAngleBox()
{
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : nFlags -
// point -
//-----------------------------------------------------------------------------
void CAngleBox::OnMouseMove(UINT nFlags, CPoint point)
{
if (m_bDragging)
{
//
// Remove old angle line by redrawing it (XOR).
//
DrawAngleLine(&m_DragDC);
//
// Calculate new yaw.
//
int nNewYaw = fixang(180 - (int)lineangle(point.x, point.y, m_ptClientCenter.x, m_ptClientCenter.y));
m_vecAngles.Init();
m_vecAngles[YAW] = nNewYaw;
//
// Draw the new angle line.
//
DrawAngleLine(&m_DragDC);
}
CWnd::OnMouseMove(nFlags, point);
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : nFlags -
// point -
//-----------------------------------------------------------------------------
void CAngleBox::OnLButtonUp(UINT nFlags, CPoint point)
{
// release dc
if (m_bDragging)
{
::ReleaseDC(m_hWnd, m_DragDC.Detach());
m_bDragging = false;
ReleaseCapture();
//
// They've explicity set the angles, so clear the different flag for
// the multiselect case.
//
SetDifferent(false);
GetParent()->PostMessage(ABN_CHANGED, GetDlgCtrlID(), 0);
}
CWnd::OnLButtonUp(nFlags, point);
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : nFlags -
// point -
//-----------------------------------------------------------------------------
void CAngleBox::OnLButtonDown(UINT nFlags, CPoint point)
{
//
// Start dragging.
//
m_DragDC.Attach(::GetDC(m_hWnd));
m_bDragging = true;
SetCapture();
CWnd::OnLButtonDown(nFlags, point);
OnMouseMove(0, point);
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : pDC -
//-----------------------------------------------------------------------------
void CAngleBox::DrawAngleLine(CDC *pDC)
{
if ((m_vecAngles[PITCH] != 0) || (m_vecAngles[ROLL] != 0) ||
(m_vecAngles[YAW] < 0 || m_vecAngles[YAW] > 359) || m_bDifferent)
{
return;
}
pDC->SetROP2(R2_XORPEN);
pDC->SelectStockObject(WHITE_PEN);
CRect r;
GetClientRect(r);
m_ptClientCenter = r.CenterPoint();
double rad = r.Width() / 2 - 3;
CPoint pt;
pt.x = m_ptClientCenter.x + sin(DEG2RAD((double)(m_vecAngles[YAW] + 90))) * rad + 0.5;
pt.y = m_ptClientCenter.y + cos(DEG2RAD((double)(m_vecAngles[YAW] + 90))) * rad + 0.5;
pDC->MoveTo(m_ptClientCenter);
pDC->LineTo(pt);
}
//-----------------------------------------------------------------------------
// Purpose: Returns the current state of the control as a keyvalue string.
// Input : szAngles - Buffer to receive angles string.
// Output : Returns 'szAngles'.
//-----------------------------------------------------------------------------
bool CAngleBox::GetAngles(QAngle &vecAngles)
{
if (m_bDifferent)
{
return false;
}
vecAngles = m_vecAngles;
return(true);
}
//-----------------------------------------------------------------------------
// Purpose: Returns the current state of the control as a keyvalue string.
// Input : szAngles - Buffer to receive angles string.
// Output : Returns 'szAngles'.
//-----------------------------------------------------------------------------
char *CAngleBox::GetAngles(char *szAngles)
{
QAngle vecAngles;
GetAngles(vecAngles);
sprintf(szAngles, "%g %g %g", (double)vecAngles[0], (double)vecAngles[1], (double)vecAngles[2]);
return(szAngles);
}
//-----------------------------------------------------------------------------
// Purpose: Returns a string indicating the current state of the angle control.
// This is used for setting the text in the companion edit control.
// Input : szBuf - Buffer to receive string.
//-----------------------------------------------------------------------------
char *CAngleBox::GetAngleEditText(char *szBuf)
{
szBuf[0] = '\0';
if (m_bDifferent)
{
strcpy(szBuf, "(diff)");
}
else if ((m_vecAngles[PITCH] == 90) && (m_vecAngles[YAW] == 0) && (m_vecAngles[ROLL] == 0))
{
strcpy(szBuf, "Down");
}
else if ((m_vecAngles[PITCH] == -90) && (m_vecAngles[YAW] == 0) && (m_vecAngles[ROLL] == 0))
{
strcpy(szBuf, "Up");
}
else if (m_vecAngles[YAW] >= 0)
{
itoa((int)m_vecAngles[YAW], szBuf, 10);
}
return(szBuf);
}
//-----------------------------------------------------------------------------
// Purpose: Called internally and by the linked combo box, this updates the angles
// without updating the linked combo box.
// Input : szAngles -
// bRedraw -
//-----------------------------------------------------------------------------
void CAngleBox::SetAnglesInternal(const QAngle &vecAngles, bool bRedraw)
{
QAngle vecAngleSet = vecAngles;
while (vecAngleSet[YAW] < 0)
{
vecAngleSet[YAW] += 360.0;
}
CDC *pDC = NULL;
if (bRedraw)
{
//
// Erase the old line.
//
Assert(::IsWindow(m_hWnd));
pDC = GetDC();
if (pDC != NULL)
{
DrawAngleLine(pDC);
}
}
//
// Update the data member.
//
m_vecAngles = vecAngleSet;
if ((bRedraw) && (pDC != NULL))
{
//
// Draw the new line.
//
DrawAngleLine(pDC);
ReleaseDC(pDC);
}
}
//-----------------------------------------------------------------------------
// Purpose: Called from the client code, this sets our angles and updates the
// linked combo box.
// Input : szAngles -
// bRedraw -
//-----------------------------------------------------------------------------
void CAngleBox::SetAngles(const QAngle &vecAngles, bool bRedraw)
{
SetAnglesInternal(vecAngles, bRedraw);
UpdateAngleEditText();
}
//-----------------------------------------------------------------------------
// Purpose: Called from the client code, this sets our angles via a string and
// updates the linked combo box.
// Input : szAngles -
// bRedraw -
//-----------------------------------------------------------------------------
void CAngleBox::SetAngles(const char *szAngles, bool bRedraw)
{
QAngle vecAngles(0, 0, 0);
sscanf(szAngles, "%f %f %f", &vecAngles[PITCH], &vecAngles[YAW], &vecAngles[ROLL]);
SetAngles(vecAngles, bRedraw);
}
//-----------------------------------------------------------------------------
// Purpose: Called internally and by the linked combo box, this sets our
// 'different' state without updating the linked combo box.
// Input : bDifferent -
// bRedraw -
//-----------------------------------------------------------------------------
void CAngleBox::SetDifferentInternal(bool bDifferent, bool bRedraw)
{
CDC *pDC = NULL;
if (bRedraw)
{
//
// Erase the old line.
//
Assert(::IsWindow(m_hWnd));
pDC = GetDC();
if (pDC != NULL)
{
DrawAngleLine(pDC);
}
}
//
// Update the data member.
//
m_bDifferent = bDifferent;
if ((bRedraw) && (pDC != NULL))
{
//
// Draw the new line.
//
DrawAngleLine(pDC);
ReleaseDC(pDC);
}
}
//-----------------------------------------------------------------------------
// Purpose: Sets our state to indicate multiselect of objects with different
// angles to avoid mucking with the angles unless they explicitly set
// them to something new.
//-----------------------------------------------------------------------------
void CAngleBox::SetDifferent(bool bDifferent, bool bRedraw)
{
SetDifferentInternal(bDifferent, bRedraw);
UpdateAngleEditText();
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CAngleBox::OnPaint(void)
{
PAINTSTRUCT ps;
CDC *pDC = BeginPaint(&ps);
if (pDC == NULL)
{
return;
}
CBrush brushWindow(GetSysColor(COLOR_3DFACE));
CBrush brushBlack(RGB(0, 0, 0));
CBrush *pBackBrush = IsWindowEnabled() ? &brushBlack : &brushWindow;
CRect r;
GetClientRect(r);
//
// Fill with the window color.
//
pDC->FillRect(&r, &brushWindow);
//
// Draw a 3D circle.
//
m_ptClientCenter = r.CenterPoint();
pDC->SelectStockObject(NULL_PEN);
pDC->SelectObject(pBackBrush);
pDC->Ellipse(r);
CPen hi(PS_SOLID, 2, GetSysColor(COLOR_3DSHADOW));
CPen lo(PS_SOLID, 2, GetSysColor(COLOR_3DHILIGHT));
pDC->SelectObject(hi);
pDC->Arc(r, CPoint(r.right, r.top), CPoint(r.left, r.bottom));
pDC->SelectObject(lo);
pDC->Arc(r, CPoint(r.left, r.bottom), CPoint(r.right, r.top));
//
// Draw center point.
//
pDC->SetPixel(m_ptClientCenter, RGB(0xff, 0xff, 0xff));
//
// Draw line indicating angles direction.
//
if (IsWindowEnabled())
{
DrawAngleLine(pDC);
}
EndPaint(&ps);
}
//-----------------------------------------------------------------------------
// Purpose: Enables or disables the angles controls.
//-----------------------------------------------------------------------------
void CAngleBox::Enable(bool bEnable)
{
if (bEnable)
{
EnableWindow(TRUE);
if (m_pEdit)
{
m_pEdit->EnableWindow(TRUE);
}
}
else
{
EnableWindow(FALSE);
if (m_pEdit)
{
m_pEdit->EnableWindow(FALSE);
}
}
Invalidate(FALSE);
UpdateWindow();
}
//-----------------------------------------------------------------------------
// Purpose: Hides or shows the angles controls.
//-----------------------------------------------------------------------------
void CAngleBox::Show(bool bShow)
{
if (bShow)
{
ShowWindow(SW_SHOW);
if (m_pEdit)
{
m_pEdit->ShowWindow(SW_SHOW);
}
}
else
{
ShowWindow(SW_HIDE);
if (m_pEdit)
{
m_pEdit->ShowWindow(SW_HIDE);
}
}
Invalidate(FALSE);
UpdateWindow();
}
//-----------------------------------------------------------------------------
// Purpose: Updates the text in the angle combo to reflect the current angles
// in the angles control.
//-----------------------------------------------------------------------------
void CAngleBox::UpdateAngleEditText(void)
{
if (m_pEdit)
{
char szBuf[20];
GetAngleEditText(szBuf);
m_pEdit->SetAnglesInternal(szBuf);
}
}
BEGIN_MESSAGE_MAP(CAngleCombo, CWnd)
//{{AFX_MSG_MAP(CAngleBox)
ON_CONTROL_REFLECT(CBN_EDITCHANGE, OnChangeAngleEdit)
ON_CONTROL_REFLECT(CBN_SELENDOK, OnSelChangeAngleEdit)
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
//-----------------------------------------------------------------------------
// Purpose: Construktor.
//-----------------------------------------------------------------------------
CAngleCombo::CAngleCombo()
: CComboBox()
{
m_pBox = NULL;
m_bEnableUpdate = true;
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : *szAngles -
//-----------------------------------------------------------------------------
void CAngleCombo::SetAnglesInternal(const char *szAngles)
{
m_bEnableUpdate = false;
SetWindowText(szAngles);
m_bEnableUpdate = true;
}
//-----------------------------------------------------------------------------
// Purpose: Handles a change in the contents of the angle edit control.
//-----------------------------------------------------------------------------
void CAngleCombo::OnChangeAngleEdit(void)
{
if (m_bEnableUpdate)
{
char buf[64];
GetWindowText(buf, 64);
UpdateAngleBox(buf);
GetParent()->PostMessage(ABN_CHANGED, GetDlgCtrlID(), 0);
}
}
//-----------------------------------------------------------------------------
// Purpose: Handles a change in the current selection of the angle edit combo.
//-----------------------------------------------------------------------------
void CAngleCombo::OnSelChangeAngleEdit(void)
{
char buf[64];
int nSel = GetCurSel();
GetLBText(nSel, buf);
UpdateAngleBox(buf);
GetParent()->PostMessage(ABN_CHANGED, GetDlgCtrlID(), 0);
}
//-----------------------------------------------------------------------------
// Purpose: Updates angle box with the settings from the combo box. Call the
// internal functions so we don't get a reflected notification, mucking
// up our state.
//-----------------------------------------------------------------------------
void CAngleCombo::UpdateAngleBox(char *szText)
{
if (m_pBox)
{
m_pBox->SetDifferentInternal(false);
if (V_isdigit(szText[0]))
{
QAngle vecAngles(0, atoi(szText), 0);
m_pBox->SetAnglesInternal(vecAngles, true);
}
else if (!stricmp(szText, "down"))
{
m_pBox->SetAnglesInternal(QAngle(90, 0, 0), true);
}
else
{
m_pBox->SetAnglesInternal(QAngle(-90, 0, 0), true);
}
}
}

130
hammer/anglebox.h Normal file
View File

@@ -0,0 +1,130 @@
//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
//=============================================================================//
#ifndef ANGLEBOX_H
#define ANGLEBOX_H
#ifdef _WIN32
#pragma once
#endif
#include "mathlib/vector.h"
class CAngleCombo;
class CAngleBox : public CWnd
{
public:
CAngleBox();
virtual ~CAngleBox();
bool GetAngles(QAngle &vecAngles);
char *GetAngles(char *szAngles);
void SetAngles(const QAngle &vecAngles, bool bRedraw = true);
void SetAngles(const char *szAngles, bool bRedraw = true);
void SetDifferent(bool bDifferent, bool bRedraw = true);
inline void SetEditControl(CAngleCombo *pEdit);
char *GetAngleEditText(char *szBuf);
void Enable(bool bEnable);
void Show(bool bShow);
// ClassWizard generated virtual function overrides
//{{AFX_VIRTUAL(CAngleBox)
public:
//}}AFX_VIRTUAL
protected:
void UpdateAngleEditText(void);
void UpdateLine(void);
void DrawAngleLine(CDC *pDC);
bool m_bDifferent; // Set to true when we have multiselected objects with different angles.
CDC m_DragDC; // When dragging w/mouse.
CPoint m_ptClientCenter;
bool m_bDragging;
QAngle m_vecAngles;
CAngleCombo *m_pEdit; // The linked angle edit box, NULL if none.
// Generated message map functions
//{{AFX_MSG(CAngleBox)
afx_msg void OnMouseMove(UINT nFlags, CPoint point);
afx_msg void OnLButtonUp(UINT nFlags, CPoint point);
afx_msg void OnLButtonDown(UINT nFlags, CPoint point);
afx_msg void OnPaint();
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
private:
friend class CAngleCombo;
// Functions called by the angle combo to set our state without notification
// back to the angle combo.
void SetAnglesInternal(const QAngle &vecAngles, bool bRedraw = true);
void SetDifferentInternal(bool bDifferent, bool bRedraw = true);
};
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CAngleBox::SetEditControl(CAngleCombo *pEdit)
{
m_pEdit = pEdit;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
class CAngleCombo : public CComboBox
{
public:
CAngleCombo();
inline void SetAngleBox(CAngleBox *pBox);
protected:
void UpdateAngleBox(char *szText);
// Generated message map functions
//{{AFX_MSG(CAngleBox)
afx_msg void OnChangeAngleEdit();
afx_msg void OnSelChangeAngleEdit();
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
private:
friend class CAngleBox;
void SetAnglesInternal(const char *szAngles);
CAngleBox *m_pBox; // The linked angle box control.
bool m_bEnableUpdate; // Whether we forward update notifications to the linked angle box control.
};
void CAngleCombo::SetAngleBox(CAngleBox *pBox)
{
m_pBox = pBox;
}
#endif // ANGLEBOX_H

291
hammer/archdlg.cpp Normal file
View File

@@ -0,0 +1,291 @@
//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ====
//
// Purpose:
//
//=============================================================================
#include "stdafx.h"
#include "hammer.h"
#include "hammer_mathlib.h"
#include "ArchDlg.h"
// memdbgon must be the last include file in a .cpp file!!!
#include <tier0/memdbgon.h>
static LPCTSTR pszSection = "Arch";
extern void MakeArc(float x1, float y1, float x2, float y2, int npoints,
float start_ang, float fArc, float points[][2]);
CArchDlg::CArchDlg(Vector& boxmins, Vector& boxmaxs, CWnd* pParent /*=NULL*/)
: CDialog(CArchDlg::IDD, pParent)
{
bmins = boxmins;
bmaxs = boxmaxs;
//{{AFX_DATA_INIT(CArchDlg)
m_iSides = 0;
m_iWallWidth = 0;
m_iAddHeight = 0;
m_fArc = 0.0f;
m_fAngle = 0.0f;
//}}AFX_DATA_INIT
// load up old defaults
CString str;
m_iWallWidth = AfxGetApp()->GetProfileInt(pszSection, "Wall Width", 32);
str = AfxGetApp()->GetProfileString(pszSection, "Arc_", "180");
m_fArc = atof(str);
m_iSides = AfxGetApp()->GetProfileInt(pszSection, "Sides", 8);
str = AfxGetApp()->GetProfileString(pszSection, "Start Angle_", "0");
m_fAngle = atof(str);
m_iAddHeight = AfxGetApp()->GetProfileInt(pszSection, "Add Height", 0);
}
void CArchDlg::SaveValues()
{
CString str;
AfxGetApp()->WriteProfileInt(pszSection, "Wall Width", m_iWallWidth);
str.Format("%f", m_fArc);
AfxGetApp()->WriteProfileString(pszSection, "Arc_", str);
AfxGetApp()->WriteProfileInt(pszSection, "Sides", m_iSides);
str.Format("%f", m_fAngle);
AfxGetApp()->WriteProfileString(pszSection, "Start Angle_", str);
AfxGetApp()->WriteProfileInt(pszSection, "Add Height", m_iAddHeight);
}
void CArchDlg::DoDataExchange(CDataExchange* pDX)
{
CDialog::DoDataExchange(pDX);
//{{AFX_DATA_MAP(CArchDlg)
DDX_Control(pDX, IDC_ANGLESPIN, m_cStartAngleSpin);
DDX_Control(pDX, IDC_WALLWIDTHSPIN, m_cWallWidthSpin);
DDX_Control(pDX, IDC_WALLWIDTH, m_cWallWidth);
DDX_Control(pDX, IDC_SIDESSPIN, m_cSidesSpin);
DDX_Control(pDX, IDC_SIDES, m_cSides);
DDX_Control(pDX, IDC_ARCSPIN, m_cArcSpin);
DDX_Control(pDX, IDC_ARC, m_cArc);
DDX_Control(pDX, IDC_PREVIEW, m_cPreview);
DDX_Text(pDX, IDC_WALLWIDTH, m_iWallWidth);
DDX_Text(pDX, IDC_SIDES, m_iSides);
DDV_MinMaxInt(pDX, m_iSides, 3, 2048);
DDX_Text(pDX, IDC_ADDHEIGHT, m_iAddHeight);
DDX_Text(pDX, IDC_ARC, m_fArc);
DDV_MinMaxFloat(pDX, m_fArc, 8.f, 360.f);
DDX_Text(pDX, IDC_ANGLE, m_fAngle);
DDV_MinMaxFloat(pDX, m_fAngle, 0.f, 360.f);
//}}AFX_DATA_MAP
}
BEGIN_MESSAGE_MAP(CArchDlg, CDialog)
//{{AFX_MSG_MAP(CArchDlg)
ON_EN_CHANGE(IDC_ARC, OnChangeArc)
ON_BN_CLICKED(IDC_CIRCLE, OnCircle)
ON_EN_UPDATE(IDC_SIDES, OnUpdateSides)
ON_EN_UPDATE(IDC_WALLWIDTH, OnUpdateWallwidth)
ON_WM_PAINT()
ON_BN_CLICKED(IDC_ARCH_PREVIEW, OnArchPreview)
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
void CArchDlg::OnChangeArc()
{
}
void CArchDlg::OnCircle()
{
m_cArcSpin.SetPos(360);
}
void CArchDlg::OnUpdateSides()
{
}
void CArchDlg::OnUpdateWallwidth()
{
}
BOOL CArchDlg::OnInitDialog()
{
CDialog::OnInitDialog();
m_cArcSpin.SetRange(8, 360);
m_cSidesSpin.SetRange(3, 100);
m_cWallWidthSpin.SetRange(2, m_iMaxWallWidth);
m_cStartAngleSpin.SetRange(0, 360);
m_cPreview.ShowWindow(SW_HIDE);
return TRUE;
}
void CArchDlg::OnPaint()
{
CPaintDC dc(this); // device context for painting
// Do not call CDialog::OnPaint() for painting messages
CBrush black(RGB(0,0,0));
CBrush grey(RGB(128,128,128));
CRect rcPreview;
m_cPreview.GetWindowRect(&rcPreview);
ScreenToClient(&rcPreview);
dc.FillRect(rcPreview, &black);
DrawArch(&dc);
rcPreview.InflateRect(1,1);
dc.FrameRect(rcPreview, &grey);
ValidateRect(rcPreview);
}
void CArchDlg::OnArchPreview()
{
//
// Build preview.
//
UpdateData(TRUE);
InvalidateRect(NULL);
UpdateWindow();
}
CArchDlg::~CArchDlg()
{
}
void CArchDlg::DrawArch(CDC* pDC)
{
int i;
float fOuterPoints[ARC_MAX_POINTS][2];
float fInnerPoints[ARC_MAX_POINTS][2];
float fScaleX;
float fScaleY;
CPen m_hPen, *pOldPen;
m_hPen.CreatePen(PS_SOLID, 1, RGB(255,255,255));
pOldPen = pDC->SelectObject(&m_hPen);
CRect rcItem;
m_cPreview.GetWindowRect(&rcItem);
ScreenToClient(&rcItem);
CPoint pt;
pt.x = rcItem.left + rcItem.Width() / 2;
pt.y = rcItem.top + rcItem.Height() / 2;
if (bmaxs[0] - bmins[0])
fScaleX = rcItem.Width()/(bmaxs[0] - bmins[0]);
else
fScaleX = 1.0f;
if (bmaxs[1] - bmins[1])
fScaleY = rcItem.Height()/(bmaxs[1] - bmins[1]);
else
fScaleY = 1.0f;
int iSides, iWallWidth;
float fArc, fStartAngle;
fArc = m_fArc;
fStartAngle = m_fAngle;
iSides = m_iSides;
iWallWidth = m_iWallWidth;
MakeArc(bmins[0], bmins[1],
bmaxs[0], bmaxs[1], iSides,
fStartAngle, fArc, fOuterPoints);
MakeArc(bmins[0] + iWallWidth,
bmins[1] + iWallWidth,
bmaxs[0] - iWallWidth,
bmaxs[1] - iWallWidth, iSides,
fStartAngle, fArc, fInnerPoints);
// check wall width - if it's half or more of the total,
// set the inner poinst to the center point of the box
// and turn off the CreateSouthFace flag
BOOL bCreateSouthFace = TRUE;
float fCenter[3];
for (i = 0; i < 3; i++)
fCenter[i] = (bmins[i] + bmaxs[i])/2.0;
if((iWallWidth*2+8) >= (bmaxs[0] - bmins[0]) ||
(iWallWidth*2+8) >= (bmaxs[1] - bmins[1]))
{
for(int i = 0; i < ARC_MAX_POINTS; i++)
{
fInnerPoints[i][0] = fCenter[0];
fInnerPoints[i][1] = fCenter[1];
}
bCreateSouthFace = FALSE;
}
for (i = 0; i < iSides; i++)
{
int iNextPoint = i+1;
if (iNextPoint >= iSides + 1)
iNextPoint = 0;
Vector points[4];
points[0][0] = fOuterPoints[i][0];
points[0][1] = fOuterPoints[i][1];
points[1][0] = fOuterPoints[iNextPoint][0];
points[1][1] = fOuterPoints[iNextPoint][1];
points[2][0] = fInnerPoints[iNextPoint][0];
points[2][1] = fInnerPoints[iNextPoint][1];
points[3][0] = fInnerPoints[i][0];
points[3][1] = fInnerPoints[i][1];
for (int j = 0; j < 4; j++)
{
points[j][0] = fScaleX * (points[j][0] - fCenter[0]);
points[j][1] = fScaleY * (points[j][1] - fCenter[1]);
}
pDC->MoveTo(pt.x + (int)points[0][0], pt.y - (int)points[0][1]);
pDC->LineTo(pt.x + (int)points[1][0], pt.y - (int)points[1][1]);
pDC->LineTo(pt.x + (int)points[2][0], pt.y - (int)points[2][1]);
pDC->LineTo(pt.x + (int)points[3][0], pt.y - (int)points[3][1]);
}
// Draw the bbox
/*CPen hPen2;
hPen2.CreatePen(PS_SOLID, 1, RGB(255,255,0));
pDC->SelectObject(&hPen2);
pDC->MoveTo(pt.x + (int)((bmins[0] - fCenter[0])*fScaleX), pt.y - (int)((bmins[1] - fCenter[1])*fScaleY));
pDC->LineTo(pt.x + (int)((bmins[0] - fCenter[0])*fScaleX), pt.y - (int)((bmaxs[1] - fCenter[1])*fScaleY));
pDC->LineTo(pt.x + (int)((bmaxs[0] - fCenter[0])*fScaleX), pt.y - (int)((bmaxs[1] - fCenter[1])*fScaleY));
pDC->LineTo(pt.x + (int)((bmaxs[0] - fCenter[0])*fScaleX), pt.y - (int)((bmins[1] - fCenter[1])*fScaleY));
*/
pDC->SelectObject(pOldPen);
}

77
hammer/archdlg.h Normal file
View File

@@ -0,0 +1,77 @@
//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ====
//
// Purpose:
//
//=============================================================================
#if !defined(AFX_ARCHDLG_H__C146AA5D_38FE_11D1_AFC9_0060979D2F4E__INCLUDED_)
#define AFX_ARCHDLG_H__C146AA5D_38FE_11D1_AFC9_0060979D2F4E__INCLUDED_
#if _MSC_VER >= 1000
#pragma once
#endif // _MSC_VER >= 1000
#define ARC_MAX_POINTS 4096
class CArchDlg : public CDialog
{
// Construction
public:
CArchDlg(Vector& bmins, Vector& bmaxs, CWnd* pParent = NULL); // standard constructor
~CArchDlg(); // standard constructor
void DrawArch(CDC *pDC);
float fOuterPoints[ARC_MAX_POINTS][2];
float fInnerPoints[ARC_MAX_POINTS][2];
Vector bmins, bmaxs;
// Dialog Data
//{{AFX_DATA(CArchDlg)
enum { IDD = IDD_ARCH };
CSpinButtonCtrl m_cStartAngleSpin;
CSpinButtonCtrl m_cWallWidthSpin;
CEdit m_cWallWidth;
CSpinButtonCtrl m_cSidesSpin;
CEdit m_cSides;
CSpinButtonCtrl m_cArcSpin;
CEdit m_cArc;
CStatic m_cPreview;
int m_iSides;
int m_iWallWidth;
int m_iAddHeight;
float m_fArc;
float m_fAngle;
//}}AFX_DATA
void SetMaxWallWidth(int iMaxWallWidth) { m_iMaxWallWidth = iMaxWallWidth; }
void SaveValues();
// Overrides
// ClassWizard generated virtual function overrides
//{{AFX_VIRTUAL(CArchDlg)
protected:
virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support
//}}AFX_VIRTUAL
// Implementation
protected:
int m_iMaxWallWidth;
// Generated message map functions
//{{AFX_MSG(CArchDlg)
afx_msg void OnChangeArc();
afx_msg void OnCircle();
afx_msg void OnUpdateSides();
afx_msg void OnUpdateWallwidth();
virtual BOOL OnInitDialog();
afx_msg void OnPaint();
afx_msg void OnArchPreview();
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
};
//{{AFX_INSERT_LOCATION}}
// Microsoft Developer Studio will insert additional declarations immediately before the previous line.
#endif // !defined(AFX_ARCHDLG_H__C146AA5D_38FE_11D1_AFC9_0060979D2F4E__INCLUDED_)

188
hammer/autoselcombo.cpp Normal file
View File

@@ -0,0 +1,188 @@
//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============//
//
// Purpose: Implements a combo box that autoselects items from the list as the
// user types in the edit control. It has the additional feature of
// being able to easily set the text color.
//
//=============================================================================//
#include "stdafx.h"
#include "AutoSelCombo.h"
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"
BEGIN_MESSAGE_MAP(CAutoSelComboBox, CComboBox)
//{{AFX_MSG_MAP(CAutoSelComboBox)
ON_WM_CTLCOLOR()
ON_CONTROL_REFLECT_EX(CBN_EDITUPDATE, OnEditUpdate)
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
//-----------------------------------------------------------------------------
// Purpose: Constructor.
//-----------------------------------------------------------------------------
CAutoSelComboBox::CAutoSelComboBox(void)
{
m_szLastText[0] = '\0';
m_dwTextColor = RGB(0, 0, 0);
m_bNotifyParent = true;
m_nLastSel = -1;
}
//-----------------------------------------------------------------------------
// Purpose: Attaches this object to the given dialog item.
//-----------------------------------------------------------------------------
void CAutoSelComboBox::SubclassDlgItem(UINT nID, CWnd *pParent)
{
//
// Disable parent notifications for CControlBar-derived classes. This is
// necessary because these classes result in multiple message reflections
// unless we return TRUE from our message handler.
//
if (pParent->IsKindOf(RUNTIME_CLASS(CControlBar)))
{
m_bNotifyParent = false;
}
else
{
m_bNotifyParent = true;
}
BaseClass::SubclassDlgItem(nID, pParent);
}
//-----------------------------------------------------------------------------
// Purpose: Automatically selects the first matching combo box item when the
// edit control's text changes.
//-----------------------------------------------------------------------------
void CAutoSelComboBox::OnUpdateText(void)
{
int nCurSel = GetCurSel();
int nNewSel = nCurSel;
DWORD dwEditSel = GetEditSel();
int nEditStart = LOWORD(dwEditSel);
int nEditEnd = HIWORD(dwEditSel);
char szTypedText[MAX_PATH];
GetWindowText(szTypedText, sizeof(szTypedText));
//
// Select the first match in the list if what they typed is different from
// the current selection. This won't do anything if they are deleting characters
// from the end of the text.
//
int nTypedLen = strlen(szTypedText);
int nLastLen = strlen(m_szLastText);
bool bSearched = false;
int nIndex = CB_ERR;
if (strnicmp(szTypedText, m_szLastText, nTypedLen))
{
nIndex = FindString(-1, szTypedText);
bSearched = true;
}
else if (nTypedLen < nLastLen)
{
// They deleted characters, try to match the shorter string exactly.
nIndex = FindStringExact(-1, szTypedText);
bSearched = true;
}
if (bSearched)
{
if (nCurSel != nIndex)
{
SetCurSel(nIndex);
nNewSel = nIndex;
}
if (nIndex != CB_ERR)
{
// Found a match.
if ((nEditEnd == -1) || (nEditEnd == (int)strlen(szTypedText)))
{
SetEditSel(strlen(szTypedText), -1);
}
else
{
SetEditSel(nEditStart, nEditEnd);
}
}
else
{
// No match found.
SetWindowText(szTypedText);
SetEditSel(nEditStart, nEditEnd);
}
}
strcpy(m_szLastText, szTypedText);
if (nNewSel != m_nLastSel)
{
GetParent()->SendMessage(WM_COMMAND, MAKEWPARAM(GetDlgCtrlID(), CBN_SELCHANGE), (LPARAM)m_hWnd);
m_nLastSel = nNewSel;
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
BOOL CAutoSelComboBox::OnEditUpdate(void)
{
OnUpdateText();
//
// Despite MSDN's lies, returning FALSE here allows the parent
// window to hook the notification message as well, not TRUE.
//
return m_bNotifyParent ? FALSE : TRUE;
}
//-----------------------------------------------------------------------------
// Purpose: Resets the 'last typed text' buffer every time we gain/lose focus.
//-----------------------------------------------------------------------------
void CAutoSelComboBox::OnSetFocus(CWnd *pOldWnd)
{
m_szLastText[0] = '\0';
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : dwColor -
//-----------------------------------------------------------------------------
void CAutoSelComboBox::SetTextColor(COLORREF dwColor)
{
m_dwTextColor = dwColor;
}
//-----------------------------------------------------------------------------
// Purpose: Called before painting to override default colors.
// Input : pDC - DEvice context being painted into.
// pWnd - Control asking for color.
// nCtlColor - Type of control asking for color.
// Output : Returns the handle of a brush to use as the background color.
//-----------------------------------------------------------------------------
HBRUSH CAutoSelComboBox::OnCtlColor(CDC *pDC, CWnd *pWnd, UINT nCtlColor)
{
HBRUSH hBrush = CComboBox::OnCtlColor(pDC, pWnd, nCtlColor);
if (nCtlColor == CTLCOLOR_EDIT)
{
pDC->SetTextColor(m_dwTextColor);
}
return(hBrush);
}

47
hammer/autoselcombo.h Normal file
View File

@@ -0,0 +1,47 @@
//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
//=============================================================================//
#ifndef AUTOSELCOMBO_H
#define AUTOSELCOMBO_H
#ifdef _WIN32
#pragma once
#endif
class CAutoSelComboBox : public CComboBox
{
typedef CComboBox BaseClass;
public:
CAutoSelComboBox(void);
void SetTextColor(COLORREF dwColor);
void SubclassDlgItem(UINT nID, CWnd *pParent);
protected:
// Called by OnEditUpdate when the user types in the edit box
virtual void OnUpdateText(void);
protected:
void OnSetFocus(CWnd *pOldWnd);
afx_msg HBRUSH OnCtlColor(CDC *pDC, CWnd *pWnd, UINT nCtlColor);
afx_msg BOOL OnEditUpdate(void);
DWORD m_dwTextColor; // RGB color of edit box text.
char m_szLastText[256]; // Last text typed by the user, for autocomplete code.
int m_nLastSel; // Index of last item we autoselected.
bool m_bNotifyParent; // Whether we allow our parent to hook our notification messages.
// This is necessary because CControlBar-derived classes result in multiple
// message reflections unless we disable parent notification.
DECLARE_MESSAGE_MAP()
};
#endif // AUTOSELCOMBO_H

39
hammer/axes2.cpp Normal file
View File

@@ -0,0 +1,39 @@
//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//=============================================================================//
#include "stdafx.h"
#include "Axes2.h"
#include "mathlib/vector.h"
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"
void Axes2::SetAxes(int h, bool bInvertH, int v, bool bInvertV)
{
bInvertHorz = bInvertH;
bInvertVert = bInvertV;
axHorz = h;
axVert = v;
if(h != AXIS_X && v != AXIS_X)
axThird = AXIS_X;
if(h != AXIS_Y && v != AXIS_Y)
axThird = AXIS_Y;
if(h != AXIS_Z && v != AXIS_Z)
axThird = AXIS_Z;
}
void Axes2::SetAxes(Axes2 &axes)
{
*this = axes;
}

40
hammer/axes2.h Normal file
View File

@@ -0,0 +1,40 @@
//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============//
//
// Purpose: Defines a base set of services for operations in an orthorgraphic
// projection. This is used as a base class for the 2D view and for
// the tools that work in the 2D views.
//
// $NoKeywords: $
//=============================================================================//
#ifndef AXES2_H
#define AXES2_H
#ifdef _WIN32
#pragma once
#endif
#include "hammer_mathlib.h"
class Axes2
{
public:
Axes2()
{
bInvertHorz = bInvertVert = false;
axHorz = AXIS_X;
axVert = AXIS_Y;
axThird = AXIS_Z;
}
void SetAxes(int h, bool bInvertH, int v, bool bInvertV);
void SetAxes(Axes2 &axes);
bool bInvertHorz; // Whether the horizontal axis is inverted.
bool bInvertVert; // Whether the vertical axis is inverted.
int axHorz; // Index of the horizontal axis (x=0, y=1, z=2)
int axVert; // Index of the vertical axis (x=0, y=1, z=2)
int axThird; // Index of the "out of the screen" axis (x=0, y=1, z=2)
};
#endif // AXES2_H

111
hammer/blockarray.cpp Normal file
View File

@@ -0,0 +1,111 @@
//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//
//=============================================================================//
#include <windows.h>
#include <stdio.h>
template <class T, int nBlockSize, int nMaxBlocks>
class BlockArray
{
public:
BlockArray()
{
nCount = nBlocks = 0;
}
~BlockArray()
{
GetBlocks(0);
}
T& operator[] (int iIndex);
void SetCount(int nObjects);
int GetCount() { return nCount; }
private:
T * Blocks[nMaxBlocks+1];
short nCount;
short nBlocks;
void GetBlocks(int nNewBlocks);
};
/*
template <class T, int nBlockSize, int nMaxBlocks>
BlockArray<T,BlockSize,nMaxBlocks>::BlockArray()
{
nCount = nBlocks = 0;
}
template <class T, int nBlockSize, int nMaxBlocks>
BlockArray<T,BlockSize,nMaxBlocks>::~BlockArray()
{
GetBlocks(0); // free blocks
}
*/
template <class T, int nBlockSize, int nMaxBlocks>
void BlockArray<T,nBlockSize,nMaxBlocks>::
GetBlocks(int nNewBlocks)
{
for(int i = nBlocks; i < nNewBlocks; i++)
{
Blocks[i] = new T[nBlockSize];
}
for(i = nNewBlocks; i < nBlocks; i++)
{
delete[] Blocks[i];
}
nBlocks = nNewBlocks;
}
template <class T, int nBlockSize, int nMaxBlocks>
void BlockArray<T,nBlockSize,nMaxBlocks>::
SetCount(int nObjects)
{
if(nObjects == nCount)
return;
// find the number of blocks required by nObjects
int nNewBlocks = (nObjects / nBlockSize) + 1;
if(nNewBlocks != nBlocks)
GetBlocks(nNewBlocks);
nCount = nObjects;
}
template <class T, int nBlockSize, int nMaxBlocks>
T& BlockArray<T,nBlockSize,nMaxBlocks>::operator[] (int iIndex)
{
if(iIndex >= nCount)
SetCount(iIndex+1);
return Blocks[iIndex / nBlockSize][iIndex % nBlockSize];
}
typedef struct
{
char Name[128];
int iValue;
} Buffy;
void main(void)
{
BlockArray<Buffy, 16, 16> Buffies;
for(int i = 0; i < 256; i++)
{
Buffies[i].iValue = i;
strcpy(Buffies[i].Name, "Buk bUk buK");
}
for(i = 0; i < 256; i++)
{
printf("%d: %s\n", Buffies[i].iValue, Buffies[i].Name);
}
Buffies.SetCount(10);
}

99
hammer/blockarray.h Normal file
View File

@@ -0,0 +1,99 @@
//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//
//=============================================================================//
#ifndef _BLOCKARRAY_H
#define _BLOCKARRAY_H
#include "tier0/dbg.h"
// memdbgon must be the last include file in a .cpp file!!!
#include <tier0/memdbgon.h>
template <class T, int nBlockSize, int nMaxBlocks>
class BlockArray
{
public:
BlockArray()
{
nCount = nBlocks = 0;
}
~BlockArray()
{
GetBlocks(0);
}
T& operator[] (int iIndex);
void SetCount(int nObjects);
int GetCount() { return nCount; }
private:
T * Blocks[nMaxBlocks+1];
short nCount;
short nBlocks;
void GetBlocks(int nNewBlocks);
};
template <class T, int nBlockSize, int nMaxBlocks>
void BlockArray<T,nBlockSize,nMaxBlocks>::
GetBlocks(int nNewBlocks)
{
for(int i = nBlocks; i < nNewBlocks; i++)
{
Blocks[i] = new T[nBlockSize];
}
for(int i = nNewBlocks; i < nBlocks; i++)
{
delete[] Blocks[i];
}
nBlocks = nNewBlocks;
}
template <class T, int nBlockSize, int nMaxBlocks>
void BlockArray<T,nBlockSize,nMaxBlocks>::
SetCount(int nObjects)
{
if(nObjects == nCount)
return;
// find the number of blocks required by nObjects, checking for
// integer rounding error
int nNewBlocks = (nObjects / nBlockSize);
if ((nNewBlocks * nBlockSize) < nObjects)
{
nNewBlocks++;
}
if(nNewBlocks != nBlocks)
{
// Make sure we don't get an overrun.
if ( nNewBlocks > ARRAYSIZE( Blocks ) )
{
Error( "BlockArray< ?, %d, %d > - too many blocks needed.", nBlockSize, nMaxBlocks );
}
GetBlocks(nNewBlocks);
}
nCount = nObjects;
}
template <class T, int nBlockSize, int nMaxBlocks>
T& BlockArray<T,nBlockSize,nMaxBlocks>::operator[] (int iIndex)
{
if(iIndex >= nCount)
{
Error( "BlockArray< %d, %d > - invalid block index.", iIndex, nCount );
SetCount(iIndex+1);
}
return Blocks[iIndex / nBlockSize][iIndex % nBlockSize];
}
#include <tier0/memdbgoff.h>
#endif // _BLOCKARRAY_H

275
hammer/boundbox.cpp Normal file
View File

@@ -0,0 +1,275 @@
//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//=============================================================================//
#include "stdafx.h"
#include "BoundBox.h"
#include "hammer_mathlib.h"
#include "MapDefs.h"
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
BoundBox::BoundBox(void)
{
ResetBounds();
}
BoundBox::BoundBox(const Vector &mins, const Vector &maxs)
{
bmins = mins;
bmaxs = maxs;
}
//-----------------------------------------------------------------------------
// Purpose: Sets the box to an uninitialized state, so that calls to UpdateBounds
// will properly set the mins and maxs.
//-----------------------------------------------------------------------------
void BoundBox::ResetBounds(void)
{
bmins[0] = bmins[1] = bmins[2] = COORD_NOTINIT;
bmaxs[0] = bmaxs[1] = bmaxs[2] = -COORD_NOTINIT;
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : pt -
//-----------------------------------------------------------------------------
void BoundBox::UpdateBounds(const Vector& pt)
{
if(pt[0] < bmins[0])
bmins[0] = pt[0];
if(pt[1] < bmins[1])
bmins[1] = pt[1];
if(pt[2] < bmins[2])
bmins[2] = pt[2];
if(pt[0] > bmaxs[0])
bmaxs[0] = pt[0];
if(pt[1] > bmaxs[1])
bmaxs[1] = pt[1];
if(pt[2] > bmaxs[2])
bmaxs[2] = pt[2];
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : bmins -
// bmaxs -
//-----------------------------------------------------------------------------
void BoundBox::UpdateBounds(const Vector& mins, const Vector& maxs)
{
if(mins[0] < bmins[0])
bmins[0] = mins[0];
if(mins[1] < bmins[1])
bmins[1] = mins[1];
if(mins[2] < bmins[2])
bmins[2] = mins[2];
if(maxs[0] > bmaxs[0])
bmaxs[0] = maxs[0];
if(maxs[1] > bmaxs[1])
bmaxs[1] = maxs[1];
if(maxs[2] > bmaxs[2])
bmaxs[2] = maxs[2];
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : pBox -
//-----------------------------------------------------------------------------
void BoundBox::UpdateBounds(const BoundBox *pBox)
{
UpdateBounds(pBox->bmins, pBox->bmaxs);
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : ptdest -
//-----------------------------------------------------------------------------
void BoundBox::GetBoundsCenter(Vector& ptdest)
{
ptdest = (bmins + bmaxs)/2.0f;
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : pt -
// Output : Returns true on success, false on failure.
//-----------------------------------------------------------------------------
bool BoundBox::ContainsPoint(const Vector& pt) const
{
for (int i = 0; i < 3; i++)
{
if (pt[i] < bmins[i] || pt[i] > bmaxs[i])
{
return(false);
}
}
return(true);
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : pfMins -
// pfMaxs -
// Output : Returns true on success, false on failure.
//-----------------------------------------------------------------------------
bool BoundBox::IsIntersectingBox(const Vector& pfMins, const Vector& pfMaxs) const
{
if ((bmins[0] >= pfMaxs[0]) || (bmaxs[0] <= pfMins[0]))
{
return(false);
}
if ((bmins[1] >= pfMaxs[1]) || (bmaxs[1] <= pfMins[1]))
{
return(false);
}
if ((bmins[2] >= pfMaxs[2]) || (bmaxs[2] <= pfMins[2]))
{
return(false);
}
return(true);
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : pfMins -
// pfMaxs -
// Output : Returns true on success, false on failure.
//-----------------------------------------------------------------------------
bool BoundBox::IsInsideBox(const Vector& pfMins, const Vector& pfMaxs) const
{
if ((bmins[0] < pfMins[0]) || (bmaxs[0] > pfMaxs[0]))
{
return(false);
}
if ((bmins[1] < pfMins[1]) || (bmaxs[1] > pfMaxs[1]))
{
return(false);
}
if ((bmins[2] < pfMins[2]) || (bmaxs[2] > pfMaxs[2]))
{
return(false);
}
return(true);
}
//-----------------------------------------------------------------------------
// Purpose: Returns whether this bounding box is valid, ie maxs >= mins.
//-----------------------------------------------------------------------------
bool BoundBox::IsValidBox(void) const
{
for (int i = 0; i < 3; i++)
{
if (bmins[i] > bmaxs[i])
{
return(false);
}
}
return(true);
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : size -
//-----------------------------------------------------------------------------
void BoundBox::GetBoundsSize(Vector& size)
{
size[0] = bmaxs[0] - bmins[0];
size[1] = bmaxs[1] - bmins[1];
size[2] = bmaxs[2] - bmins[2];
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : iValue -
// iGridSize -
// Output :
//-----------------------------------------------------------------------------
static int Snap(/*int*/ float iValue, int iGridSize)
{
return (int)(rint(iValue/iGridSize) * iGridSize);
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : iGridSize -
//-----------------------------------------------------------------------------
void BoundBox::SnapToGrid(int iGridSize)
{
// does not alter the size of the box .. snaps its minimal coordinates
// to the grid size specified in iGridSize
Vector size;
GetBoundsSize(size);
for(int i = 0; i < 3; i++)
{
bmins[i] = (float)Snap(/* YWB (int)*/bmins[i], iGridSize);
bmaxs[i] = bmins[i] + size[i];
}
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : axis -
//-----------------------------------------------------------------------------
void BoundBox::Rotate90(int axis)
{
int e1 = AXIS_X, e2 = AXIS_Y;
// get bounds center first
Vector center;
GetBoundsCenter(center);
switch(axis)
{
case AXIS_Z:
e1 = AXIS_X;
e2 = AXIS_Y;
break;
case AXIS_X:
e1 = AXIS_Y;
e2 = AXIS_Z;
break;
case AXIS_Y:
e1 = AXIS_X;
e2 = AXIS_Z;
break;
}
float tmp1, tmp2;
tmp1 = bmins[e1] - center[e1] + center[e2];
tmp2 = bmaxs[e1] - center[e1] + center[e2];
bmins[e1] = bmins[e2] - center[e2] + center[e1];
bmaxs[e1] = bmaxs[e2] - center[e2] + center[e1];
bmins[e2] = tmp1;
bmaxs[e2] = tmp2;
}

71
hammer/boundbox.h Normal file
View File

@@ -0,0 +1,71 @@
//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============//
//
// Purpose: An axis aligned bounding box class.
//
// $NoKeywords: $
//=============================================================================//
#ifndef BOUNDBOX_H
#define BOUNDBOX_H
#ifdef _WIN32
#pragma once
#endif
#include "mathlib/vector.h"
class BoundBox
{
public:
BoundBox(void);
BoundBox(const Vector &mins, const Vector &maxs);
void ResetBounds(void);
inline void SetBounds(const Vector &mins, const Vector &maxs);
void UpdateBounds(const Vector& bmins, const Vector& bmaxs);
void UpdateBounds(const Vector& pt);
void UpdateBounds(const BoundBox *pBox);
void GetBoundsCenter(Vector& ptdest);
inline void GetBounds(Vector& Mins, Vector& Maxs) const;
virtual bool IsIntersectingBox(const Vector& pfMins, const Vector& pfMaxs) const;
bool IsInsideBox(const Vector& pfMins, const Vector& pfMaxs) const;
bool ContainsPoint(const Vector& pt) const;
bool IsValidBox(void) const;
void GetBoundsSize(Vector& size);
void SnapToGrid(int iGridSize);
void Rotate90(int axis);
Vector bmins;
Vector bmaxs;
};
//-----------------------------------------------------------------------------
// Purpose: Gets the bounding box as two vectors, a min and a max.
// Input : Mins - Receives the box's minima.
// Maxs - Receives the box's maxima.
//-----------------------------------------------------------------------------
void BoundBox::GetBounds(Vector &Mins, Vector &Maxs) const
{
Mins = bmins;
Maxs = bmaxs;
}
//-----------------------------------------------------------------------------
// Purpose: Sets the box outright, equivalent to ResetBounds + UpdateBounds.
// Input : mins - Minima to set.
// maxs - Maxima to set.
//-----------------------------------------------------------------------------
void BoundBox::SetBounds(const Vector &mins, const Vector &maxs)
{
bmins = mins;
bmaxs = maxs;
}
#endif // BOUNDBOX_H

1179
hammer/box3d.cpp Normal file

File diff suppressed because it is too large Load Diff

150
hammer/box3d.h Normal file
View File

@@ -0,0 +1,150 @@
//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//=============================================================================//
#ifndef BOX3D_H
#define BOX3D_H
#pragma once
#include "Tool3D.h"
#include "BoundBox.h"
class CMapView2D;
class CRender3D;
//
// Formats for displaying world units.
//
enum WorldUnits_t
{
Units_None,
Units_Inches,
Units_Feet_Inches,
};
class Box3D : public Tool3D, public BoundBox
{
public:
Box3D(void);
static inline void SetWorldUnits(WorldUnits_t eWorldUnits);
static inline WorldUnits_t GetWorldUnits(void);
//
// CBaseTool implementation.
//
virtual void SetEmpty();
virtual void RenderTool2D(CRender2D *pRender);
virtual void RenderTool3D(CRender3D *pRender);
virtual void UpdateStatusBar();
protected:
enum
{
expandbox = 0x01,
thicklines = 0x04,
boundstext = 0x08,
};
enum TransformMode_t
{
modeNone = 0,
modeMove,
modeScale,
modeRotate,
modeShear,
modeLast,
};
void StartNew( CMapView *pView, const Vector2D &vPoint, const Vector &vecStart, const Vector &vecSize);
inline int GetTranslateMode() { return m_TranslateMode; }
virtual void ToggleTranslateMode(void);
void EnableHandles(bool bEnable);
void SetDrawFlags(DWORD dwFlags);
DWORD GetDrawFlags() { return m_dwDrawFlags; }
void SetDrawColors(COLORREF dwHandleColor, COLORREF dwBoxColor);
virtual void GetStatusString(char *pszBuf);
unsigned long UpdateCursor(CMapView *pView, const Vector &vHandleHit, TransformMode_t eTransformMode);
void HandleToWorld( Vector &vWorld, const Vector &vHandle, const Vector *pCustomHandleBox = NULL);
const Vector NearestCorner(const Vector2D &vPoint, CMapView *pView, const Vector *pCustomHandleBox = NULL);
int GetVisibleHandles( Vector *handles, CMapView *, int nMode );
void RenderHandles2D(CRender2D *pRender, const Vector &mins, const Vector &maxs );
void RenderHandles3D(CRender3D *pRender, const Vector &mins, const Vector &maxs);
//
// Tool3D implementation.
//
public:
virtual int HitTest(CMapView *pView, const Vector2D &ptClient, bool bTestHandles = false);
// If pCustomHandleBox is non-null, it points at an array 2 vectors (min and max), and
// it will use those bounds to figure out the corners that it will align to the grid.
virtual void StartTranslation( CMapView *pView, const Vector2D &vPoint, const Vector &vHandleOrigin, const Vector *pRefPoint = NULL, const Vector *pCustomHandleBox = NULL );
virtual bool UpdateTranslation(const Vector &vUpdate, UINT uConstraints);
virtual void FinishTranslation(bool bSave);
virtual void TranslatePoint(Vector& pt);
void TranslateBox(Vector& mins, Vector& maxs);
virtual const VMatrix& GetTransformMatrix();
protected:
void UpdateTransformMatrix();
static WorldUnits_t m_eWorldUnits;
COLORREF m_clrHandle;
COLORREF m_clrBox;
TransformMode_t m_TranslateMode; // current translation mode
Vector m_TranslateHandle; // current translation handle/corner
Vector m_vTranslationFixPoint; // fix point, meaning it remains unchanged by translation, eg rotation center etc.
VMatrix m_TransformMatrix;
bool m_bEnableHandles; // check/show handles yes/no
Vector m_LastHitTestHandle; // handle hit by last HitTest call
TransformMode_t m_LastTranslateMode; // last translate mode
bool m_bPreventOverlap;
DWORD m_dwDrawFlags;
};
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
WorldUnits_t Box3D::GetWorldUnits(void)
{
return(m_eWorldUnits);
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void Box3D::SetWorldUnits(WorldUnits_t eWorldUnits)
{
m_eWorldUnits = eWorldUnits;
}
#endif // BOX3D_H

353
hammer/brushops.cpp Normal file
View File

@@ -0,0 +1,353 @@
//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//=============================================================================//
#include "stdafx.h"
#include <stdio.h>
#include <math.h>
#include "hammer.h"
#include "MapEntity.h"
#include "MapDefs.h"
#include "MapFace.h"
#include "hammer_mathlib.h"
#include "history.h"
#include "Error3d.h"
#include "BrushOps.h"
#include "GlobalFunctions.h"
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"
#pragma warning( disable : 4244 ) // Disable warning messages
#define SIDE_FRONT 0
#define SIDE_BACK 1
#define SIDE_ON 2
#define BOGUS_RANGE ( MAX_COORD_INTEGER * 4 )
float lightaxis[3] = {1, 0.6f, 0.75f};
const int MAX_POINTS_ON_WINDING = 128;
void Error(char* fmt, ...)
{
char str[300];
sprintf(str, fmt, (&fmt)+1);
Msg(mwError, str);
}
/*
=============================================================================
TURN PLANES INTO GROUPS OF FACES
=============================================================================
*/
/*
==================
NewWinding
==================
*/
winding_t *NewWinding (int points)
{
winding_t *w;
if (points > MAX_POINTS_ON_WINDING)
Error ("NewWinding: %i points", points);
w = (winding_t *)malloc(sizeof(*w));
w->numpoints = 0; // None are occupied yet even though allocated.
w->p = (Vector *)calloc( points, sizeof(Vector) );
return w;
}
void FreeWinding (winding_t *w)
{
if (*(unsigned *)w == 0xdeaddead)
Error ("FreeWinding: freed a freed winding");
*(unsigned *)w = 0xdeaddead;
if (w->p)
{
free (w->p);
w->p = NULL;
}
free (w);
}
size_t WindingSize(int points)
{
return (size_t)(&((winding_t *)0)->p[points]);
}
//-----------------------------------------------------------------------------
// Purpose: Removes points that are withing a given distance from each other
// from the winding.
// Input : pWinding - The winding to remove duplicates from.
// fMinDist - The minimum distance two points must be from one another
// to be considered different. If this is zero, the points must be
// identical to be considered duplicates.
//-----------------------------------------------------------------------------
void RemoveDuplicateWindingPoints(winding_t *pWinding, float fMinDist)
{
for (int i = 0; i < pWinding->numpoints; i++)
{
for (int j = i + 1; j < pWinding->numpoints; j++)
{
Vector edge;
VectorSubtract(pWinding->p[i], pWinding->p[j], edge);
if (VectorLength(edge) < fMinDist)
{
if (j + 1 < pWinding->numpoints)
{
memmove(&pWinding->p[j], &pWinding->p[j + 1], (pWinding->numpoints - (j + 1)) * sizeof(pWinding->p[0]));
}
pWinding->numpoints--;
}
}
}
}
/*
==================
CopyWinding
==================
*/
winding_t *CopyWinding (winding_t *w)
{
int size;
winding_t *c;
c = NewWinding (w->numpoints);
c->numpoints = w->numpoints;
size = w->numpoints*sizeof(w->p[0]);
memcpy (c->p, w->p, size);
return c;
}
/*
==================
ClipWinding
Clips the winding to the plane, returning the new winding on the positive side
Frees the input winding.
==================
*/
// YWB ADDED SPLIT EPS to match qcsg splitting
#define SPLIT_EPSILON 0.01
winding_t *ClipWinding (winding_t *in, PLANE *split)
{
float dists[MAX_POINTS_ON_WINDING];
int sides[MAX_POINTS_ON_WINDING];
int counts[3];
float dot;
int i, j;
Vector *p1, *p2, *mid;
winding_t *neww;
int maxpts;
counts[0] = counts[1] = counts[2] = 0;
// determine sides for each point
for (i=0 ; i<in->numpoints ; i++)
{
dot = DotProduct (in->p[i], split->normal);
dot -= split->dist;
dists[i] = dot;
if (dot > SPLIT_EPSILON)
sides[i] = SIDE_FRONT;
else if (dot < -SPLIT_EPSILON)
sides[i] = SIDE_BACK;
else
{
sides[i] = SIDE_ON;
}
counts[sides[i]]++;
}
sides[i] = sides[0];
dists[i] = dists[0];
if (!counts[0] && !counts[1])
return in;
if (!counts[0])
{
free (in);
return NULL;
}
if (!counts[1])
return in;
maxpts = in->numpoints+4; // can't use counts[0]+2 because
// of fp grouping errors
neww = NewWinding (maxpts);
for (i=0 ; i<in->numpoints ; i++)
{
p1 = &in->p[i];
mid = &neww->p[neww->numpoints];
if (sides[i] == SIDE_FRONT || sides[i] == SIDE_ON)
{
*mid = *p1;
neww->numpoints++;
if (sides[i] == SIDE_ON)
continue;
mid = &neww->p[neww->numpoints];
}
if (sides[i+1] == SIDE_ON || sides[i+1] == sides[i])
continue;
// generate a split point
if (i == in->numpoints - 1)
p2 = &in->p[0];
else
p2 = p1 + 1;
neww->numpoints++;
dot = dists[i] / (dists[i]-dists[i+1]);
for (j=0 ; j<3 ; j++)
{ // avoid round off error when possible
if (split->normal[j] == 1)
mid[0][j] = split->dist;
else if (split->normal[j] == -1)
mid[0][j] = -split->dist;
mid[0][j] = p1[0][j] + dot*(p2[0][j]-p1[0][j]);
}
// mid[3] = p1[3] + dot*(p2[3]-p1[3]);
// mid[4] = p1[4] + dot*(p2[4]-p1[4]);
}
if (neww->numpoints > maxpts)
Error ("ClipWinding: points exceeded estimate");
// free the original winding
FreeWinding (in);
return neww;
}
//-----------------------------------------------------------------------------
// Purpose: Creates a huge quadrilateral winding given a plane.
// Input : pPlane - Plane normal and distance to use when creating the winding.
// Output : Returns a winding with 4 points.
//-----------------------------------------------------------------------------
// dvs: read through this and clean it up
winding_t *CreateWindingFromPlane(PLANE *pPlane)
{
int i, x;
float max, v;
Vector org, vright, vup;
winding_t *w;
// find the major axis
max = -BOGUS_RANGE;
x = -1;
for (i=0 ; i<3; i++)
{
v = fabs(pPlane->normal[i]);
if (v > max)
{
x = i;
max = v;
}
}
if (x==-1)
Error ("BasePolyForPlane: no axis found");
vup = vec3_origin;
switch (x)
{
case 0:
case 1:
vup[2] = 1;
break;
case 2:
vup[0] = 1;
break;
}
v = DotProduct (vup, pPlane->normal);
VectorMA (vup, -v, pPlane->normal, vup);
VectorNormalize (vup);
org = pPlane->normal * pPlane->dist;
CrossProduct (vup, pPlane->normal, vright);
vup = vup * MAX_TRACE_LENGTH;
vright = vright * MAX_TRACE_LENGTH;
// project a really big axis aligned box onto the plane
w = NewWinding (4);
w->numpoints = 4;
VectorSubtract (org, vright, w->p[0]);
VectorAdd (w->p[0], vup, w->p[0]);
VectorAdd (org, vright, w->p[1]);
VectorAdd (w->p[1], vup, w->p[1]);
VectorAdd (org, vright, w->p[2]);
VectorSubtract (w->p[2], vup, w->p[2]);
VectorSubtract (org, vright, w->p[3]);
VectorSubtract (w->p[3], vup, w->p[3]);
return w;
}
static CArray<error3d, error3d&> Errors;
static int nErrors;
void Add3dError(DWORD dwObjectID, LPCTSTR pszReason, PVOID pInfo)
{
error3d err;
err.dwObjectID = dwObjectID;
err.pszReason = pszReason;
err.pInfo = pInfo;
Errors.Add(err);
++nErrors;
}
int Get3dErrorCount()
{
return nErrors;
}
error3d * Enum3dErrors(BOOL bStart)
{
static int iCurrent = 0;
if(bStart)
iCurrent = 0;
if(iCurrent == nErrors)
return NULL;
return & Errors.GetData()[iCurrent++];
}

33
hammer/brushops.h Normal file
View File

@@ -0,0 +1,33 @@
//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
//=============================================================================//
#ifndef BRUSHOPS_H
#define BRUSHOPS_H
#ifdef _WIN32
#pragma once
#endif
#include "MapFace.h"
#define ON_PLANE_EPSILON 0.5f // Vertices must be within this many units of the plane to be considered on the plane.
#define MIN_EDGE_LENGTH_EPSILON 0.1f // Edges shorter than this are considered degenerate.
#define ROUND_VERTEX_EPSILON 0.01f // Vertices within this many units of an integer value will be rounded to an integer value.
void Add3dError(DWORD dwObjectID, LPCTSTR pszReason, PVOID pInfo);
winding_t *ClipWinding(winding_t *in, PLANE *split);
winding_t *CopyWinding(winding_t *w);
winding_t *NewWinding(int points);
void FreeWinding (winding_t *w);
winding_t *CreateWindingFromPlane(PLANE *pPlane);
size_t WindingSize(int points);
void RemoveDuplicateWindingPoints(winding_t *pWinding, float fMinDist = 0);
#endif // BRUSHOPS_H

969
hammer/bsplighting.cpp Normal file
View File

@@ -0,0 +1,969 @@
//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//=============================================================================//
#include "stdafx.h"
#include "materialsystem/imaterialsystem.h"
#include "istudiorender.h"
#include "material.h"
#include "materialsystem/imesh.h"
#include "disp_common.h"
#include "bsplighting.h"
#include "interface.h"
#include "filesystem.h"
#include "hammer.h"
#include "tier0/dbg.h"
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"
bool SurfHasBumpedLightmaps( int flags )
{
return ( flags & SURF_BUMPLIGHT ) &&
( !( flags & SURF_NOLIGHT ) );
}
void InitLMSamples( Vector4D *pSamples, int nSamples, float value )
{
for( int i=0; i < nSamples; i++ )
{
pSamples[i][0] = pSamples[i][1] = pSamples[i][2] = value;
pSamples[i][3] = 1.0f;
}
}
void InitLMSamplesRed( Vector4D *pSamples, int nSamples )
{
for( int i=0; i < nSamples; i++ )
{
pSamples[i][0] = 1;
pSamples[i][1] = pSamples[i][2] = 0;
pSamples[i][3] = 1.0f;
}
}
CBSPLighting::CMaterialBuf::CMaterialBuf()
{
m_nVerts = m_nIndices = 0;
m_pMesh = NULL;
}
CBSPLighting::CMaterialBuf::~CMaterialBuf()
{
if( m_pMesh )
{
CMatRenderContextPtr pRenderContext( MaterialSystemInterface() );
pRenderContext->DestroyStaticMesh( m_pMesh );
}
m_DrawCommands.PurgeAndDeleteElements();
}
CBSPLighting::CFaceMaterial::~CFaceMaterial()
{
m_MaterialBufs.PurgeAndDeleteElements();
m_Faces.PurgeAndDeleteElements();
}
CBSPLighting::CBSPLighting()
{
m_nTotalTris = 0;
m_hVRadDLL = 0;
m_pVRadDLL = 0;
m_pBSPLightingThread = 0;
m_bLightingInProgress = false;
}
CBSPLighting::~CBSPLighting()
{
Term();
}
void CBSPLighting::Release()
{
delete this;
}
bool CBSPLighting::Load( char const *pFilename )
{
// Free everything.
Term();
// Load VRAD's DLL (and load the BSP file).
if( !LoadVRADDLL( pFilename ) )
return false;
// Create the lighting thread.
m_pBSPLightingThread = CreateBSPLightingThread( m_pVRadDLL );
if( !m_pBSPLightingThread )
return false;
// Get the BSP file information from VRAD.
CBSPInfo file;
m_pVRadDLL->GetBSPInfo( &file );
// Allocate faces and verts.
CUtlVector<char> usedFaces;
usedFaces.SetSize( file.numfaces );
int nFaces = 0;
int nVerts = 0;
for( int iCountFace=0; iCountFace < file.numfaces; iCountFace++ )
{
usedFaces[iCountFace] = 0;
if( file.dfaces[iCountFace].m_LightmapTextureSizeInLuxels[0] != 0 ||
file.dfaces[iCountFace].m_LightmapTextureSizeInLuxels[0] != 0 )
{
texinfo_t *pTexInfo = &file.texinfo[ file.dfaces[iCountFace].texinfo ];
if( !(pTexInfo->flags & SURF_NODRAW) )
{
++nFaces;
nVerts += file.dfaces[iCountFace].numedges;
usedFaces[iCountFace] = 1;
}
}
}
CUtlVector<CFace> faces;
faces.SetSize( nFaces );
CUtlVector<CVert> verts;
verts.SetSize( nVerts );
m_StoredFaces.SetSize( nFaces );
InitMaterialLUT( file );
// Make lightmaps and translate the map faces over..
IMaterialSystem *pMatSys = MaterialSystemInterface();
// Add the BSP file as a search path so our FindMaterial calls will get
// VMFs embedded in the BSP file.
g_pFullFileSystem->AddSearchPath( pFilename, "GAME" );
m_nTotalTris = 0;
int iOutVert = 0;
int iOutFace = 0;
for( int iFace=0; iFace < file.numfaces; iFace++ )
{
dface_t *pIn = &file.dfaces[iFace];
if( !usedFaces[iFace] )
{
continue;
}
CFace *pOut = &faces[iOutFace];
CStoredFace *pStoredFace = &m_StoredFaces[iOutFace];
++iOutFace;
pStoredFace->m_iMapFace = iFace;
pStoredFace->m_pFace = pOut;
pOut->m_pDFace = pIn;
pOut->m_pStoredFace = pStoredFace;
// Get its material.
texinfo_t *pTexInfo = &file.texinfo[pIn->texinfo];
dtexdata_t *pTexData = &file.dtexdata[pTexInfo->texdata];
pStoredFace->m_pMaterial = FindOrAddMaterial( file, pTexData->nameStringTableID );
if( pStoredFace->m_pMaterial )
pStoredFace->m_pMaterial->m_Faces.AddToTail( pStoredFace );
// Setup its lightmap.
memcpy( pOut->m_LightmapVecs, file.texinfo[pIn->texinfo].lightmapVecsLuxelsPerWorldUnits, sizeof(pOut->m_LightmapVecs) );
memcpy( pOut->m_LightmapTextureMinsInLuxels, pIn->m_LightmapTextureMinsInLuxels, sizeof(pOut->m_LightmapTextureMinsInLuxels) );
pStoredFace->m_LightmapSize[0] = pIn->m_LightmapTextureSizeInLuxels[0]+1;
pStoredFace->m_LightmapSize[1] = pIn->m_LightmapTextureSizeInLuxels[1]+1;
// Setup the verts.
pOut->m_iVertStart = iOutVert;
pOut->m_nVerts = pIn->numedges;
for( int iEdge=0; iEdge < pIn->numedges; iEdge++ )
{
int edgeVal = file.dsurfedges[ pIn->firstedge + iEdge ];
if( edgeVal < 0 )
verts[pOut->m_iVertStart+iEdge].m_vPos = file.dvertexes[ file.dedges[-edgeVal].v[1] ].point;
else
verts[pOut->m_iVertStart+iEdge].m_vPos = file.dvertexes[ file.dedges[edgeVal].v[0] ].point;
}
m_nTotalTris += pOut->m_nVerts - 2;
iOutVert += pOut->m_nVerts;
pOut->m_iDispInfo = pIn->dispinfo;
}
g_pFullFileSystem->RemoveSearchPath( pFilename, "GAME" );
// Allocate lightmaps.. must be grouped by material.
pMatSys->ResetMaterialLightmapPageInfo();
pMatSys->BeginLightmapAllocation();
FOR_EACH_LL( m_FaceMaterials, iMat )
{
CFaceMaterial *pMat = m_FaceMaterials[iMat];
bool bNeedsBumpmap = pMat->m_pMaterial->GetPropertyFlag( MATERIAL_PROPERTY_NEEDS_BUMPED_LIGHTMAPS );
FOR_EACH_LL( pMat->m_Faces, iFace )
{
CStoredFace *pStoredFace = pMat->m_Faces[iFace];
CFace *pOut = pStoredFace->m_pFace;
int bumpedSize = pStoredFace->m_LightmapSize[0];
if( bNeedsBumpmap )
bumpedSize *= 4;
pOut->m_LightmapSortID = pMatSys->AllocateLightmap(
bumpedSize,
pStoredFace->m_LightmapSize[1],
pStoredFace->m_OffsetIntoLightmapPage,
pMat->m_pMaterial );
}
}
pMatSys->EndLightmapAllocation();
// Get sort IDs from the material system.
CUtlVector<MaterialSystem_SortInfo_t> sortInfos;
sortInfos.SetSize( pMatSys->GetNumSortIDs() );
pMatSys->GetSortInfo( sortInfos.Base() );
for( int iFace=0; iFace < faces.Count(); iFace++ )
{
m_StoredFaces[iFace].m_LightmapPageID = sortInfos[faces[iFace].m_LightmapSortID].lightmapPageID;
}
// Setup the gamma table.
BuildGammaTable( 2.2f, 2.2f, 0, 1 );
// Set lightmap texture coordinates.
for( int iFace=0; iFace < faces.Count(); iFace++ )
{
CFace *pFace = &faces[iFace];
CStoredFace *pStoredFace = &m_StoredFaces[iFace];
texinfo_t *pTexInfo = &file.texinfo[pFace->m_pDFace->texinfo];
int lightmapPageSize[2];
pMatSys->GetLightmapPageSize( pFace->m_pStoredFace->m_LightmapPageID, &lightmapPageSize[0], &lightmapPageSize[1] );
pStoredFace->m_BumpSTexCoordOffset = (float)pStoredFace->m_LightmapSize[0] / lightmapPageSize[0];
// Set its texture coordinates.
for( int iVert=0; iVert < pFace->m_nVerts; iVert++ )
{
CVert *pVert = &verts[ pFace->m_iVertStart + iVert ];
Vector &vPos = pVert->m_vPos;
for( int iCoord=0; iCoord < 2; iCoord++ )
{
float *lmVec = pFace->m_LightmapVecs[iCoord];
float flVal = lmVec[0]*vPos[0] + lmVec[1]*vPos[1] + lmVec[2]*vPos[2] + lmVec[3] - pFace->m_LightmapTextureMinsInLuxels[iCoord];
flVal += pFace->m_pStoredFace->m_OffsetIntoLightmapPage[iCoord];
flVal += 0.5f; // bilinear...
flVal /= lightmapPageSize[iCoord];
Assert( _finite(flVal) );
pVert->m_vLightCoords[iCoord] = flVal;
pVert->m_vTexCoords[iCoord] =
DotProduct( vPos, *((Vector*)pTexInfo->textureVecsTexelsPerWorldUnits[iCoord]) ) +
pTexInfo->textureVecsTexelsPerWorldUnits[iCoord][3];
if( pStoredFace->m_pMaterial )
{
if( iCoord == 0 )
pVert->m_vTexCoords[iCoord] /= pStoredFace->m_pMaterial->m_pMaterial->GetMappingWidth();
else
pVert->m_vTexCoords[iCoord] /= pStoredFace->m_pMaterial->m_pMaterial->GetMappingHeight();
}
}
}
}
// Create displacements.
CUtlVector<CDispInfoFaces> dispInfos;
CreateDisplacements( file, faces, dispInfos );
BuildLMGroups( file, faces, verts, dispInfos );
BuildDrawCommands();
ReloadLightmaps();
return true;
}
void CBSPLighting::Term()
{
if( m_pBSPLightingThread )
{
m_pBSPLightingThread->Release();
m_pBSPLightingThread = 0;
}
m_nTotalTris = 0;
if( m_hVRadDLL )
{
if( m_pVRadDLL )
{
// Save the .r0 and .bsp files.
m_pVRadDLL->Serialize();
m_pVRadDLL->Release();
m_pVRadDLL = 0;
}
Sys_UnloadModule( m_hVRadDLL );
m_hVRadDLL = 0;
}
m_StoredFaces.Purge();
}
bool CBSPLighting::Serialize()
{
if( m_pBSPLightingThread )
{
// Only serialize if we're not currently in the middle of lighting.
if( m_pBSPLightingThread->GetCurrentState() == IBSPLightingThread::STATE_FINISHED )
return m_pVRadDLL->Serialize();
}
return false;
}
void CBSPLighting::StartLighting( char const *pVMFFileWithEnts )
{
if( m_pBSPLightingThread )
{
m_pBSPLightingThread->StartLighting( pVMFFileWithEnts );
m_bLightingInProgress = true;
}
}
float CBSPLighting::GetPercentComplete()
{
if( m_bLightingInProgress && m_pBSPLightingThread )
return m_pBSPLightingThread->GetPercentComplete();
else
return -1;
}
void CBSPLighting::Interrupt()
{
if( m_pBSPLightingThread )
m_pBSPLightingThread->Interrupt();
}
bool CBSPLighting::CheckForNewLightmaps()
{
if( !m_pBSPLightingThread )
return false;
// Has it finished lighting?
int curState = m_pBSPLightingThread->GetCurrentState();
if( m_bLightingInProgress )
{
if( curState == IBSPLightingThread::STATE_FINISHED )
{
m_bLightingInProgress = false;
ReloadLightmaps();
return true;
}
else if( curState == IBSPLightingThread::STATE_IDLE )
{
m_bLightingInProgress = false;
}
}
return false;
}
#define DRAWLIGHTMAPPAGE
#if defined( DRAWLIGHTMAPPAGE )
void DrawLightmapPage( IMaterialSystem *materialSystemInterface, int lightmapPageID )
{
IMaterial *g_materialDebugLightmap = materialSystemInterface->FindMaterial( "debug/debuglightmap", TEXTURE_GROUP_OTHER );
// assumes that we are already in ortho mode.
int lightmapPageWidth, lightmapPageHeight;
CMatRenderContextPtr pRenderContext( materialSystemInterface );
IMesh* pMesh = pRenderContext->GetDynamicMesh( true, NULL, NULL, g_materialDebugLightmap );
materialSystemInterface->GetLightmapPageSize( lightmapPageID, &lightmapPageWidth, &lightmapPageHeight );
pRenderContext->BindLightmapPage( lightmapPageID );
CMeshBuilder meshBuilder;
meshBuilder.Begin( pMesh, MATERIAL_QUADS, 1 );
// texcoord 1 is lightmaptexcoord for fixed function.
static int yOffset = 30;
meshBuilder.TexCoord2f( 1, 0.0f, 0.0f );
meshBuilder.Position3f( 0.0f, yOffset, 0.0f );
meshBuilder.AdvanceVertex();
meshBuilder.TexCoord2f( 1, 1.0f, 0.0f );
meshBuilder.Position3f( lightmapPageWidth, yOffset, 0.0f );
meshBuilder.AdvanceVertex();
meshBuilder.TexCoord2f( 1, 1.0f, 1.0f );
meshBuilder.Position3f( lightmapPageWidth, yOffset+lightmapPageHeight, 0.0f );
meshBuilder.AdvanceVertex();
meshBuilder.TexCoord2f( 1, 0.0f, 1.0f );
meshBuilder.Position3f( 0.0f, yOffset+lightmapPageHeight, 0.0f );
meshBuilder.AdvanceVertex();
meshBuilder.End();
pMesh->Draw();
}
#endif
void CBSPLighting::Draw()
{
if( m_FaceMaterials.Count() == 0 )
return;
IMaterialSystem *pMatSys = MaterialSystemInterface();
if( !pMatSys )
return;
CMatRenderContextPtr pRenderContext( pMatSys );
CheckForNewLightmaps();
pRenderContext->Flush();
#if defined( DRAWLIGHTMAPPAGE )
static bool bDrawIt = false;
if( bDrawIt )
{
pRenderContext->MatrixMode( MATERIAL_VIEW );
pRenderContext->PushMatrix();
pRenderContext->LoadIdentity();
pRenderContext->MatrixMode( MATERIAL_MODEL );
pRenderContext->PushMatrix();
pRenderContext->LoadIdentity();
pRenderContext->MatrixMode( MATERIAL_PROJECTION );
pRenderContext->PushMatrix();
pRenderContext->LoadIdentity();
pRenderContext->Ortho( 0, 0, 300, 300, -99999, 99999 );
static int iPageToDraw = 0;
DrawLightmapPage( MaterialSystemInterface(), iPageToDraw );
pRenderContext->MatrixMode( MATERIAL_VIEW );
pRenderContext->PopMatrix();
pRenderContext->MatrixMode( MATERIAL_MODEL );
pRenderContext->PopMatrix();
pRenderContext->MatrixMode( MATERIAL_PROJECTION );
pRenderContext->PopMatrix();
}
#endif
// Draw everything from each material.
FOR_EACH_LL( m_FaceMaterials, iMat )
{
CFaceMaterial *pMat = m_FaceMaterials[iMat];
pRenderContext->Bind( pMat->m_pMaterial );
FOR_EACH_LL( pMat->m_MaterialBufs, iBuf )
{
CMaterialBuf *pBuf = pMat->m_MaterialBufs[iBuf];
for( int iCmd=0; iCmd < pBuf->m_DrawCommands.Count(); iCmd++ )
{
CDrawCommand *pCmd = pBuf->m_DrawCommands[iCmd];
pRenderContext->BindLightmapPage( pCmd->m_LightmapPageID );
pBuf->m_pMesh->Draw( pCmd->m_PrimLists.Base(), pCmd->m_PrimLists.Count() );
}
}
}
pRenderContext->Flush();
}
void CBSPLighting::AssignFaceMaterialCounts(
CBSPInfo &file,
CUtlVector<CFace> &faces )
{
FOR_EACH_LL( m_FaceMaterials, i )
{
CFaceMaterial *pMat = m_FaceMaterials[i];
// Start off an initial CMaterialBuf to dump the faces in.
CMaterialBuf *pBuf = new CMaterialBuf;
pMat->m_MaterialBufs.AddToTail( pBuf );
FOR_EACH_LL( pMat->m_Faces, iFace )
{
CStoredFace *pStoredFace = pMat->m_Faces[iFace];
CFace *pFace = pStoredFace->m_pFace;
pStoredFace->m_iFirstIndex = pBuf->m_nIndices;
if( pFace->m_iDispInfo == -1 )
{
pStoredFace->m_nIndices = (pFace->m_nVerts - 2) * 3;
pBuf->m_nIndices += (pFace->m_nVerts - 2) * 3;
pBuf->m_nVerts += pFace->m_nVerts;
}
else
{
ddispinfo_t *pDisp = &file.g_dispinfo[pFace->m_iDispInfo];
int nTris = Square( 1 << pDisp->power ) * 2;
int nVerts = Square( (1 << pDisp->power) + 1 );
pStoredFace->m_nIndices = nTris * 3;
pBuf->m_nIndices += nTris * 3;
pBuf->m_nVerts += nVerts;
}
pBuf->m_Faces.AddToTail( pStoredFace );
// Don't make the buffers too big..
if( pBuf->m_nIndices > (16*1024) || pBuf->m_nVerts > (16*1024) )
{
pBuf = new CMaterialBuf;
pMat->m_MaterialBufs.AddToTail( pBuf );
}
}
}
}
//-----------------------------------------------------------------------------
// Determines the appropriate vertex format for LMGroup meshes
//-----------------------------------------------------------------------------
VertexFormat_t CBSPLighting::ComputeLMGroupVertexFormat( IMaterial * pMaterial )
{
VertexFormat_t vertexFormat = pMaterial->GetVertexFormat();
// FIXME: set VERTEX_FORMAT_COMPRESSED if there are no artifacts and if it saves enough memory (use 'mem_dumpvballocs')
vertexFormat &= ~VERTEX_FORMAT_COMPRESSED;
// FIXME: check for and strip unused vertex elements (bone weights+indices, TANGENT_S/T?) - requires reliable material vertex formats first
return vertexFormat;
}
void CBSPLighting::BuildLMGroups(
CBSPInfo &file,
CUtlVector<CFace> &faces,
CUtlVector<CVert> &verts,
CUtlVector<CDispInfoFaces> &dispInfos
)
{
// Count everything in each CFaceMaterial.
AssignFaceMaterialCounts( file, faces );
IMaterialSystem *pMatSys = MaterialSystemInterface();
if( !pMatSys )
return;
CMatRenderContextPtr pRenderContext( pMatSys );
// Now create the static buffers.
FOR_EACH_LL( m_FaceMaterials, iMat )
{
CFaceMaterial *pMat = m_FaceMaterials[iMat];
FOR_EACH_LL( pMat->m_MaterialBufs, iBuf )
{
CMaterialBuf *pBuf = pMat->m_MaterialBufs[iBuf];
VertexFormat_t vertexFormat = ComputeLMGroupVertexFormat( pMat->m_pMaterial );
pBuf->m_pMesh = pRenderContext->CreateStaticMesh( vertexFormat, "terd", pMat->m_pMaterial );
if( !pBuf->m_pMesh )
continue;
bool bNeedsBumpmap = pMat->m_pMaterial->GetPropertyFlag( MATERIAL_PROPERTY_NEEDS_BUMPED_LIGHTMAPS );
CMeshBuilder mb;
mb.Begin( pBuf->m_pMesh, MATERIAL_TRIANGLES, pBuf->m_nVerts, pBuf->m_nIndices );
// Write all the faces in.
int iCurBaseVert = 0;
FOR_EACH_LL( pBuf->m_Faces, iFace )
{
CStoredFace *pStoredFace = pBuf->m_Faces[iFace];
CFace *pFace = pStoredFace->m_pFace;
if( pFace->m_iDispInfo == -1 )
{
// It's a regular face.
CVert *pVerts = &verts[pFace->m_iVertStart];
for( int iVert=0; iVert < pFace->m_nVerts; iVert++ )
{
mb.Position3fv( (float*)&pVerts[iVert].m_vPos );
mb.TexCoord2fv( 0, pVerts[iVert].m_vTexCoords.Base() );
mb.TexCoord2fv( 1, pVerts[iVert].m_vLightCoords.Base() );
if( bNeedsBumpmap )
mb.TexCoord2f ( 2, pStoredFace->m_BumpSTexCoordOffset, 0 );
mb.Color3f( 1,1,1 );
mb.AdvanceVertex();
}
// Write the indices.
for( int iTri=0; iTri < pFace->m_nVerts-2; iTri++ )
{
mb.Index( iCurBaseVert ); mb.AdvanceIndex();
mb.Index( iCurBaseVert+iTri+1 ); mb.AdvanceIndex();
mb.Index( iCurBaseVert+iTri+2 ); mb.AdvanceIndex();
}
iCurBaseVert += pFace->m_nVerts;
}
else
{
// It's a displacement.
CDispInfoFaces *pDisp = &dispInfos[pFace->m_iDispInfo];
// Generate the index list.
unsigned short indices[ (1<<MAX_MAP_DISP_POWER) * (1<<MAX_MAP_DISP_POWER) * 6 ];
int nRequired = DispCommon_GetNumTriIndices( pDisp->m_Power );
Assert( nRequired <= sizeof(indices)/sizeof(indices[0]) );
DispCommon_GenerateTriIndices( pDisp->m_Power, indices );
for( int iIndex=0; iIndex < nRequired; iIndex++ )
{
mb.Index( indices[iIndex] + iCurBaseVert );
mb.AdvanceIndex();
}
// Generate the vert list.
for( int iVert=0; iVert < pDisp->m_Verts.Count(); iVert++ )
{
mb.Position3fv( (float*)&pDisp->m_Verts[iVert].m_vPos );
mb.TexCoord2fv( 0, (float*)&pDisp->m_Verts[iVert].m_vTexCoords );
mb.TexCoord2fv( 1, (float*)&pDisp->m_Verts[iVert].m_vLightCoords );
if( bNeedsBumpmap )
mb.TexCoord2f ( 2, pStoredFace->m_BumpSTexCoordOffset, 0 );
mb.AdvanceVertex();
}
iCurBaseVert += pDisp->m_Verts.Count();;
}
}
mb.End();
}
}
}
bool FindDrawCommand( CUtlVector<CBSPLighting::CDrawCommand*> &drawCommands, int lmPageID, int &index )
{
for( int i=0; i < drawCommands.Count(); i++ )
{
if( drawCommands[i]->m_LightmapPageID == lmPageID )
{
index = i;
return true;
}
}
return false;
}
void CBSPLighting::BuildDrawCommands()
{
FOR_EACH_LL( m_FaceMaterials, iMat )
{
CFaceMaterial *pMat = m_FaceMaterials[iMat];
FOR_EACH_LL( pMat->m_MaterialBufs, iBuf )
{
CMaterialBuf *pBuf = pMat->m_MaterialBufs[iBuf];
// Group by lightmap page IDs.
FOR_EACH_LL( pBuf->m_Faces, iFace )
{
CStoredFace *pFace = pBuf->m_Faces[iFace];
int index;
if( !FindDrawCommand( pBuf->m_DrawCommands, pFace->m_LightmapPageID, index ) )
{
index = pBuf->m_DrawCommands.AddToTail( new CDrawCommand );
pBuf->m_DrawCommands[index]->m_LightmapPageID = pFace->m_LightmapPageID;
}
CPrimList primList;
primList.m_FirstIndex = pFace->m_iFirstIndex;
primList.m_NumIndices = pFace->m_nIndices;
pBuf->m_DrawCommands[index]->m_PrimLists.AddToTail( primList );
}
}
}
}
void CBSPLighting::ReloadLightmaps()
{
if( !m_pVRadDLL )
return;
IMaterialSystem *pMatSys = MaterialSystemInterface();
if( !pMatSys )
return;
CBSPInfo bspInfo;
m_pVRadDLL->GetBSPInfo( &bspInfo );
if( !bspInfo.lightdatasize )
return;
Vector4D blocklights[4][MAX_LIGHTMAP_DIM_INCLUDING_BORDER * MAX_LIGHTMAP_DIM_INCLUDING_BORDER];
for( int iFace=0; iFace < m_StoredFaces.Count(); iFace++ )
{
CStoredFace *pFace = &m_StoredFaces[iFace];
// Avoid updating lightmaps in faces that weren't touched.
if( bspInfo.m_pFacesTouched && !bspInfo.m_pFacesTouched[pFace->m_iMapFace] )
continue;
dface_t *pIn = &bspInfo.dfaces[ pFace->m_iMapFace ];
int nLuxels = pFace->m_LightmapSize[0] * pFace->m_LightmapSize[1];
bool bNeedsBumpmap = pFace->m_pMaterial->m_pMaterial->GetPropertyFlag( MATERIAL_PROPERTY_NEEDS_BUMPED_LIGHTMAPS );
texinfo_t *pTexInfo = &bspInfo.texinfo[ bspInfo.dfaces[pFace->m_iMapFace].texinfo ];
bool bHasBumpmap = SurfHasBumpedLightmaps( pTexInfo->flags );
int nLightmaps = 1;
if( bNeedsBumpmap && bHasBumpmap )
nLightmaps = 4;
ColorRGBExp32 *pLightmap = (ColorRGBExp32 *)&bspInfo.dlightdata[pIn->lightofs];
int iLightmap;
for( iLightmap=0; iLightmap < nLightmaps; iLightmap++ )
{
for( int iLuxel=0; iLuxel < nLuxels; iLuxel++ )
{
blocklights[iLightmap][iLuxel][0] = TexLightToLinear( pLightmap->r, pLightmap->exponent );
blocklights[iLightmap][iLuxel][1] = TexLightToLinear( pLightmap->g, pLightmap->exponent );
blocklights[iLightmap][iLuxel][2] = TexLightToLinear( pLightmap->b, pLightmap->exponent );
blocklights[iLightmap][iLuxel][3] = 1;
++pLightmap;
}
}
// If it needs bumpmaps but doesn't have them in the file, then just copy
// the lightmap data into the other lightmaps like the engine does.
if( bNeedsBumpmap && !bHasBumpmap )
{
for( iLightmap=1; iLightmap < 4; iLightmap++ )
{
memcpy( blocklights[iLightmap], blocklights[0], nLuxels * sizeof( blocklights[0][0] ) );
}
}
if( bNeedsBumpmap )
{
pMatSys->UpdateLightmap(
pFace->m_LightmapPageID,
pFace->m_LightmapSize,
pFace->m_OffsetIntoLightmapPage,
(float*)blocklights[0], (float*)blocklights[1], (float*)blocklights[2], (float*)blocklights[3] );
}
else
{
pMatSys->UpdateLightmap(
pFace->m_LightmapPageID,
pFace->m_LightmapSize,
pFace->m_OffsetIntoLightmapPage,
(float*)blocklights[0], NULL, NULL, NULL );
}
}
}
bool CBSPLighting::LoadVRADDLL( char const *pFilename )
{
// Load VRAD's DLL.
m_hVRadDLL = Sys_LoadModule( "vrad_dll.dll" );
if( !m_hVRadDLL )
return false;
CreateInterfaceFn fn = Sys_GetFactory( m_hVRadDLL );
if( !fn )
return false;
int retCode = 0;
m_pVRadDLL = (IVRadDLL*)fn( VRAD_INTERFACE_VERSION, &retCode );
if( !m_pVRadDLL )
return false;
// Tell VRAD to load the BSP file.
if( !m_pVRadDLL->Init( pFilename ) )
return false;
return true;
}
void CBSPLighting::CreateDisplacements( CBSPInfo &file, CUtlVector<CFace> &faces, CUtlVector<CDispInfoFaces> &dispInfos )
{
/*
IMaterialSystem *pMatSys = MaterialSystemInterface();
dispInfos.SetSize( file.g_numdispinfo );
for( int iFace=0; iFace < faces.Size(); iFace++ )
{
CFace *pFace = &faces[iFace];
CStoredFace *pStoredFace = &m_StoredFaces[iFace];
dface_t *pInFace = pFace->m_pDFace;
if( pInFace->dispinfo == -1 )
continue;
ddispinfo_t *pInDisp = &file.g_dispinfo[pInFace->dispinfo];
CDispInfoFaces *pOutDisp = &dispInfos[pInFace->dispinfo];
pOutDisp->m_Power = pInDisp->power;
int nVertsPerSide = (1 << pInDisp->power) + 1;
pOutDisp->m_Verts.SetSize( pInDisp->m_LODs[0].m_nVerts );
int lightmapPageSize[2];
pMatSys->GetLightmapPageSize( pFace->m_pStoredFace->m_LightmapPageID, &lightmapPageSize[0], &lightmapPageSize[1] );
for( int iVert=0; iVert < pInDisp->m_LODs[0].m_nVerts; iVert++ )
{
ddisp_lod_vert_t *pInVert = &file.ddispverts[ pInDisp->m_LODs[0].m_iVertStart + iVert ];
CVert *pOutVert = &pOutDisp->m_Verts[iVert];
pOutVert->m_vPos = pInVert->m_vPos;
for( int iCoord=0; iCoord < 2; iCoord++ )
{
float flVal = pInVert->m_LightCoords[iCoord];
flVal += pFace->m_pStoredFace->m_OffsetIntoLightmapPage[iCoord];
flVal += 0.5f;
flVal /= lightmapPageSize[iCoord];
Assert( _finite(flVal) );
pOutVert->m_vLightCoords[iCoord] = flVal;
pOutVert->m_vTexCoords[iCoord] = pInVert->m_TexCoords[iCoord];
if( iCoord == 0 )
pOutVert->m_vTexCoords[iCoord] /= pStoredFace->m_pMaterial->m_pMaterial->GetMappingWidth();
else
pOutVert->m_vTexCoords[iCoord] /= pStoredFace->m_pMaterial->m_pMaterial->GetMappingHeight();
}
}
}
*/
}
void CBSPLighting::InitMaterialLUT( CBSPInfo &file )
{
m_StringTableIDToMaterial.SetSize( file.nTexDataStringTable );
for( int i=0; i < m_StringTableIDToMaterial.Count(); i++ )
m_StringTableIDToMaterial[i] = 0;
}
CBSPLighting::CFaceMaterial* CBSPLighting::FindOrAddMaterial( CBSPInfo &file, int stringTableID )
{
if( stringTableID >= m_StringTableIDToMaterial.Count() )
{
Assert( false );
return 0;
}
if( m_StringTableIDToMaterial[stringTableID] )
{
return m_StringTableIDToMaterial[stringTableID];
}
else
{
IMaterial *pMaterial = 0;
char *pMaterialName = &file.texDataStringData[ file.texDataStringTable[ stringTableID ] ];
if( pMaterialName )
pMaterial = MaterialSystemInterface()->FindMaterial( pMaterialName, TEXTURE_GROUP_OTHER );
// Don't add CFaceMaterials without a material.
if( !pMaterial )
return 0;
// This is lovely. We have to call this stuff to get it to precalculate the data it needs.
pMaterial->GetMappingHeight();
pMaterial->RecomputeStateSnapshots();
CFaceMaterial *pMat = new CFaceMaterial;
if( pMaterial->IsTranslucent() )
m_FaceMaterials.AddToTail( pMat );
else
m_FaceMaterials.AddToHead( pMat );
pMat->m_pMaterial = pMaterial;
m_StringTableIDToMaterial[stringTableID] = pMat;
return pMat;
}
}
IBSPLighting* CreateBSPLighting()
{
return new CBSPLighting;
}

186
hammer/bsplighting.h Normal file
View File

@@ -0,0 +1,186 @@
//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//=============================================================================//
#ifndef BSPLIGHTING_H
#define BSPLIGHTING_H
#ifdef _WIN32
#pragma once
#endif
#include "stdafx.h"
#include "ibsplighting.h"
#include "utlvector.h"
#include "utllinkedlist.h"
#include "bspfile.h"
#include "interface.h"
#include "ivraddll.h"
#include "ibsplightingthread.h"
class CBSPLighting : public IBSPLighting
{
public:
CBSPLighting();
virtual ~CBSPLighting();
virtual void Release();
virtual bool Load( char const *pFilename );
virtual void Term();
virtual bool Serialize();
virtual void StartLighting( char const *pVMFFileWithEnts );
virtual float GetPercentComplete();
virtual void Interrupt();
virtual bool CheckForNewLightmaps();
virtual void Draw();
private:
class CVert
{
public:
Vector m_vPos;
Vector2D m_vTexCoords;
Vector2D m_vLightCoords;
};
// This is the face data we store for each face. Just enough to
// let us update the lightmaps in memory.
class CFaceMaterial;
class CFace;
class CStoredFace
{
public:
int m_iMapFace; // index into dfaces.
int m_LightmapPageID;
int m_OffsetIntoLightmapPage[2];
int m_LightmapSize[2]; // This already has 1 added to it (unlike dface).
CFaceMaterial *m_pMaterial;
CFace *m_pFace; // only valid inside of Load
float m_BumpSTexCoordOffset;
// Indices into CFaceMaterial::m_pMesh
int m_iFirstIndex;
int m_nIndices;
};
class CDrawCommand
{
public:
CUtlVector<CPrimList> m_PrimLists;
int m_LightmapPageID;
};
friend bool FindDrawCommand( CUtlVector<CDrawCommand*> &drawCommands, int lmPageID, int &index );
class CMaterialBuf
{
public:
CMaterialBuf();
~CMaterialBuf();
CUtlLinkedList<CStoredFace*, unsigned short> m_Faces;
// Commands to draw everything in this material as fast as possible.
CUtlVector<CDrawCommand*> m_DrawCommands;
int m_nVerts;
int m_nIndices;
IMesh *m_pMesh;
};
class CFaceMaterial
{
public:
~CFaceMaterial();
IMaterial *m_pMaterial;
// Faces using this material.
CUtlLinkedList<CStoredFace*, unsigned short> m_Faces;
// Static buffers to hold all the verts.
CUtlLinkedList<CMaterialBuf*, unsigned short> m_MaterialBufs;
};
class CFace
{
public:
int m_iDispInfo;
dface_t *m_pDFace; // used while loading..
CStoredFace *m_pStoredFace;
int m_LightmapSortID;
float m_LightmapVecs[2][4];
int m_LightmapTextureMinsInLuxels[2];
int m_iVertStart; // Indexes CBSPLighting::m_Verts.
int m_nVerts;
};
class CDispInfoFaces
{
public:
CUtlVector<CVert> m_Verts;
int m_Power;
};
private:
void AssignFaceMaterialCounts(
CBSPInfo &file,
CUtlVector<CFace> &faces );
VertexFormat_t ComputeLMGroupVertexFormat( IMaterial * pMaterial );
void BuildLMGroups(
CBSPInfo &file,
CUtlVector<CFace> &faces,
CUtlVector<CVert> &verts,
CUtlVector<CDispInfoFaces> &dispInfos
);
void BuildDrawCommands();
void ReloadLightmaps();
bool LoadVRADDLL( char const *pFilename );
void CreateDisplacements( CBSPInfo &file, CUtlVector<CFace> &faces, CUtlVector<CDispInfoFaces> &dispInfos );
// Fast material ID to CFaceMaterial lookups..
void InitMaterialLUT( CBSPInfo &file );
CFaceMaterial* FindOrAddMaterial( CBSPInfo &file, int stringTableID );
private:
CUtlVector<CStoredFace> m_StoredFaces;
CUtlLinkedList<CFaceMaterial*, unsigned short> m_FaceMaterials;
int m_nTotalTris;
// The VRAD DLL. This holds the level file.
CSysModule *m_hVRadDLL;
IVRadDLL *m_pVRadDLL;
// The lighting thread.
IBSPLightingThread *m_pBSPLightingThread;
// Used to detect when lighting is finished so it can update the lightmaps
// in the material system.
bool m_bLightingInProgress;
// Maps string table IDs to materials.
CUtlVector<CFaceMaterial*> m_StringTableIDToMaterial;
};
#endif // BSPLIGHTING_H

View File

@@ -0,0 +1,227 @@
//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//=============================================================================//
#include "stdafx.h"
#include "bsplightingthread.h"
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"
// --------------------------------------------------------------------------- //
// Global functions.
// --------------------------------------------------------------------------- //
IBSPLightingThread* CreateBSPLightingThread( IVRadDLL *pDLL )
{
CBSPLightingThread *pRet = new CBSPLightingThread;
if( pRet->Init( pDLL ) )
{
return pRet;
}
else
{
delete pRet;
return 0;
}
}
DWORD WINAPI ThreadMainLoop_Static( LPVOID lpParameter )
{
return ((CBSPLightingThread*)lpParameter)->ThreadMainLoop();
}
// --------------------------------------------------------------------------- //
// Static helpers.
// --------------------------------------------------------------------------- //
class CCSLock
{
public:
CCSLock( CRITICAL_SECTION *pCS )
{
EnterCriticalSection( pCS );
m_pCS = pCS;
}
~CCSLock()
{
LeaveCriticalSection( m_pCS );
}
CRITICAL_SECTION *m_pCS;
};
// --------------------------------------------------------------------------- //
//
// --------------------------------------------------------------------------- //
CBSPLightingThread::CBSPLightingThread()
{
InitializeCriticalSection( &m_CS );
m_hThread = 0;
m_ThreadID = 0;
m_ThreadCmd = THREADCMD_NONE;
m_ThreadState = STATE_IDLE;
}
CBSPLightingThread::~CBSPLightingThread()
{
if( m_hThread )
{
// Stop the current lighting process if one is going on.
Interrupt();
// Tell the thread to exit.
SetThreadCmd( THREADCMD_EXIT );
DWORD dwCode;
while( 1 )
{
if( GetExitCodeThread( m_hThread, &dwCode ) && dwCode == 0 )
break;
Sleep( 10 );
}
CloseHandle( m_hThread );
}
DeleteCriticalSection( &m_CS );
}
void CBSPLightingThread::Release()
{
delete this;
}
void CBSPLightingThread::StartLighting( char const *pVMFFileWithEntities )
{
// First, kill any lighting going on.
Interrupt();
// Store the VMF file data for the thread.
int len = strlen( pVMFFileWithEntities ) + 1;
m_VMFFileWithEntities.CopyArray( pVMFFileWithEntities, len );
// Tell the thread to start lighting.
SetThreadState( STATE_LIGHTING );
SetThreadCmd( THREADCMD_LIGHT );
}
int CBSPLightingThread::GetCurrentState()
{
return GetThreadState();
}
void CBSPLightingThread::Interrupt()
{
if( GetThreadState() == STATE_LIGHTING )
{
m_pVRadDLL->Interrupt();
while( GetThreadState() == STATE_LIGHTING )
Sleep( 10 );
}
}
float CBSPLightingThread::GetPercentComplete()
{
return m_pVRadDLL->GetPercentComplete();
}
bool CBSPLightingThread::Init( IVRadDLL *pDLL )
{
m_pVRadDLL = pDLL;
m_hThread = CreateThread(
NULL,
0,
ThreadMainLoop_Static,
this,
0,
&m_ThreadID );
if( !m_hThread )
return false;
SetThreadPriority( m_hThread, THREAD_PRIORITY_LOWEST );
return true;
}
DWORD CBSPLightingThread::ThreadMainLoop()
{
while( 1 )
{
int cmd = GetThreadCmd();
if( cmd == THREADCMD_NONE )
{
// Keep waiting for a new command.
Sleep( 10 );
}
else if( cmd == THREADCMD_LIGHT )
{
if( m_pVRadDLL->DoIncrementalLight( m_VMFFileWithEntities.Base() ) )
SetThreadState( STATE_FINISHED );
else
SetThreadState( STATE_IDLE );
}
else if( cmd == THREADCMD_EXIT )
{
return 0;
}
}
}
int CBSPLightingThread::GetThreadCmd()
{
CCSLock lock( &m_CS );
int ret = m_ThreadCmd;
m_ThreadCmd = THREADCMD_NONE;
return ret;
}
void CBSPLightingThread::SetThreadCmd( int cmd )
{
CCSLock lock( &m_CS );
m_ThreadCmd = cmd;
}
int CBSPLightingThread::GetThreadState()
{
CCSLock lock( &m_CS );
return m_ThreadState;
}
void CBSPLightingThread::SetThreadState( int state )
{
CCSLock lock( &m_CS );
m_ThreadState = state;
}

View File

@@ -0,0 +1,87 @@
//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//=============================================================================//
#ifndef BSPLIGHTINGTHREAD_H
#define BSPLIGHTINGTHREAD_H
#ifdef _WIN32
#pragma once
#endif
#include "stdafx.h"
#include "ibsplightingthread.h"
#include "utlvector.h"
class CBSPLightingThread : public IBSPLightingThread
{
public:
CBSPLightingThread();
// IBSPLightingThread functions.
public:
virtual ~CBSPLightingThread();
virtual void Release();
virtual void StartLighting( char const *pVMFFileWithEntities );
virtual int GetCurrentState();
virtual void Interrupt();
virtual float GetPercentComplete();
// Other functions.
public:
// This is called immediately after the constructor. It creates the thread.
bool Init( IVRadDLL *pDLL );
// Threadsafe functions.
public:
enum
{
THREADCMD_NONE=0,
THREADCMD_LIGHT=1,
THREADCMD_EXIT=2
};
// Get the current command to the thread. Resets to THREADCMD_NONE on exit.
int GetThreadCmd();
void SetThreadCmd( int cmd );
// Returns an IBSPLightingThread::STATE_ define.
int GetThreadState();
void SetThreadState( int state );
public:
// The thread's run function.
DWORD ThreadMainLoop();
public:
int m_ThreadCmd; // Next command for the thread to run.
int m_ThreadState; // Current state of the thread.
CUtlVector<char> m_VMFFileWithEntities;
public:
IVRadDLL *m_pVRadDLL;
HANDLE m_hThread;
DWORD m_ThreadID;
CRITICAL_SECTION m_CS;
};
#endif // BSPLIGHTINGTHREAD_H

Some files were not shown because too many files have changed in this diff Show More