You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
546 lines
15 KiB
546 lines
15 KiB
//========= Copyright 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() |
|
{ |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
//----------------------------------------------------------------------------- |
|
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 ) |
|
{ |
|
CMapOverlay *pOverlay = dynamic_cast<CMapOverlay*>( pChildren->Element(pos) ); |
|
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 ) |
|
{ |
|
CMapOverlay *pOverlay = dynamic_cast<CMapOverlay*>( pChildren->Element(pos2) ); |
|
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 = pSelection->Element( iSelection ); |
|
if ( pMapClass && pMapClass->IsMapClass( MAPCLASS_TYPE( CMapEntity ) ) ) |
|
{ |
|
const CMapObjectList *pChildren = pMapClass->GetChildren(); |
|
FOR_EACH_OBJ( *pChildren, pos ) |
|
{ |
|
CMapOverlay *pOverlay = dynamic_cast<CMapOverlay*>( pChildren->Element(pos) ); |
|
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 = pSelection->Element( iSelection ); |
|
if ( pMapClass && pMapClass->IsMapClass( MAPCLASS_TYPE( CMapEntity ) ) ) |
|
{ |
|
const CMapObjectList *pChildren = pMapClass->GetChildren(); |
|
FOR_EACH_OBJ( *pChildren, pos ) |
|
{ |
|
CMapOverlay *pOverlay = dynamic_cast<CMapOverlay*>( pChildren->Element(pos) ); |
|
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 posWorld; |
|
CMapClass *pChild = pWorld->GetFirstDescendent( posWorld ); |
|
while ( pChild ) |
|
{ |
|
CMapEntity *pEntity = dynamic_cast<CMapEntity*>( pChild ); |
|
if ( pEntity ) |
|
{ |
|
const CMapObjectList *pChildren = pEntity->GetChildren(); |
|
FOR_EACH_OBJ( *pChildren, pos ) |
|
{ |
|
CMapOverlay *pOverlay = dynamic_cast<CMapOverlay*>( pChildren->Element(pos) ); |
|
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( posWorld ); |
|
} |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
//----------------------------------------------------------------------------- |
|
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 ); |
|
} |
|
} |
|
}
|
|
|