summaryrefslogtreecommitdiff
path: root/hammer/ToolOverlay.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'hammer/ToolOverlay.cpp')
-rw-r--r--hammer/ToolOverlay.cpp546
1 files changed, 546 insertions, 0 deletions
diff --git a/hammer/ToolOverlay.cpp b/hammer/ToolOverlay.cpp
new file mode 100644
index 0000000..b0d5b40
--- /dev/null
+++ b/hammer/ToolOverlay.cpp
@@ -0,0 +1,546 @@
+//========= 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 );
+ }
+ }
+}