Team Fortress 2 Source Code as on 22/4/2020
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

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. // $NoKeywords: $
  6. //=============================================================================//
  7. #include <stdafx.h>
  8. #include "MapWorld.h"
  9. #include "GlobalFunctions.h"
  10. #include "MainFrm.h"
  11. #include "ToolOverlay.h"
  12. #include "MapDoc.h"
  13. #include "History.h"
  14. #include "CollisionUtils.h"
  15. #include "cmodel.h"
  16. #include "MapView3D.h"
  17. #include "MapView2D.h"
  18. #include "MapSolid.h"
  19. #include "Camera.h"
  20. #include "ObjectProperties.h" // FIXME: For ObjectProperties::RefreshData
  21. #include "Selection.h"
  22. // memdbgon must be the last include file in a .cpp file!!!
  23. #include <tier0/memdbgon.h>
  24. #define OVERLAY_TOOL_SNAP_DISTANCE 35.0f
  25. //-----------------------------------------------------------------------------
  26. // Purpose: Constructor
  27. //-----------------------------------------------------------------------------
  28. CToolOverlay::CToolOverlay()
  29. {
  30. m_bDragging = false;
  31. m_pActiveOverlay = NULL;
  32. }
  33. //-----------------------------------------------------------------------------
  34. // Purpose: Destructor
  35. //-----------------------------------------------------------------------------
  36. CToolOverlay::~CToolOverlay()
  37. {
  38. }
  39. //-----------------------------------------------------------------------------
  40. //-----------------------------------------------------------------------------
  41. void CToolOverlay::OnActivate()
  42. {
  43. m_bDragging = false;
  44. m_pActiveOverlay = NULL;
  45. }
  46. //-----------------------------------------------------------------------------
  47. //-----------------------------------------------------------------------------
  48. void CToolOverlay::OnDeactivate()
  49. {
  50. }
  51. //-----------------------------------------------------------------------------
  52. //-----------------------------------------------------------------------------
  53. bool CToolOverlay::OnLMouseUp3D( CMapView3D *pView, UINT nFlags, const Vector2D &vPoint )
  54. {
  55. // Post drag events.
  56. PostDrag();
  57. // Update the entity properties dialog.
  58. GetMainWnd()->pObjectProperties->MarkDataDirty();
  59. return true;
  60. }
  61. //-----------------------------------------------------------------------------
  62. //-----------------------------------------------------------------------------
  63. bool CToolOverlay::OnLMouseDown3D( CMapView3D *pView, UINT nFlags, const Vector2D &vPoint )
  64. {
  65. // Handle the overlay "handle" selection.
  66. if ( HandleSelection( pView, vPoint ) )
  67. {
  68. PreDrag();
  69. return true;
  70. }
  71. // Handle adding and removing overlay entities from the selection list.
  72. OverlaySelection( pView, nFlags, vPoint );
  73. // Handle the overlay creation and placement (if we hit a solid).
  74. ULONG ulFace;
  75. CMapClass *pObject = NULL;
  76. if ( ( pObject = pView->NearestObjectAt( vPoint, ulFace ) ) != NULL )
  77. {
  78. CMapSolid *pSolid = dynamic_cast<CMapSolid*>( pObject );
  79. if ( pSolid )
  80. {
  81. return CreateOverlay( pSolid, ulFace, pView, vPoint );
  82. }
  83. }
  84. return true;
  85. }
  86. //-----------------------------------------------------------------------------
  87. //-----------------------------------------------------------------------------
  88. bool CToolOverlay::OnMouseMove3D( CMapView3D *pView, UINT nFlags, const Vector2D &vPoint )
  89. {
  90. if ( m_bDragging )
  91. {
  92. bool bShift = ( ( GetKeyState( VK_SHIFT ) & 0x8000 ) != 0 );
  93. // Build the ray and drag the overlay handle to the impact point.
  94. const CCamera *pCamera = pView->GetCamera();
  95. if ( pCamera )
  96. {
  97. Vector vecStart, vecEnd;
  98. pView->BuildRay( vPoint, vecStart, vecEnd );
  99. OnDrag( vecStart, vecEnd, bShift );
  100. }
  101. }
  102. return true;
  103. }
  104. //-----------------------------------------------------------------------------
  105. //-----------------------------------------------------------------------------
  106. bool CToolOverlay::CreateOverlay( CMapSolid *pSolid, ULONG iFace, CMapView3D *pView, Vector2D point )
  107. {
  108. // Build a ray to trace against the face that they clicked on to
  109. // find the point of intersection.
  110. Vector vecStart, vecEnd;
  111. pView->BuildRay( point, vecStart, vecEnd );
  112. Vector vecHitPos, vecHitNormal;
  113. CMapFace *pFace = pSolid->GetFace( iFace );
  114. if( pFace->TraceLine( vecHitPos, vecHitNormal, vecStart, vecEnd ) )
  115. {
  116. // Create and initialize the "entity" --> "overlay."
  117. CMapEntity *pEntity = new CMapEntity;
  118. pEntity->SetKeyValue( "material", GetDefaultTextureName() );
  119. pEntity->SetPlaceholder( TRUE );
  120. pEntity->SetOrigin( vecHitPos );
  121. pEntity->SetClass( "info_overlay" );
  122. // Add the entity to the world.
  123. m_pDocument->AddObjectToWorld( pEntity );
  124. // Setup "history."
  125. GetHistory()->MarkUndoPosition( NULL, "Create Overlay" );
  126. GetHistory()->KeepNew( pEntity );
  127. // Initialize the overlay.
  128. InitOverlay( pEntity, pFace );
  129. pEntity->CalcBounds( TRUE );
  130. // Add to selection list.
  131. m_pDocument->SelectObject( pEntity, scSelect );
  132. m_bEmpty = false;
  133. // Set modified and update views.
  134. m_pDocument->SetModifiedFlag();
  135. m_pShoreline = NULL;
  136. return true;
  137. }
  138. return false;
  139. }
  140. //-----------------------------------------------------------------------------
  141. // Purpose:
  142. //-----------------------------------------------------------------------------
  143. void CToolOverlay::InitOverlay( CMapEntity *pEntity, CMapFace *pFace )
  144. {
  145. // Valid face?
  146. if ( !pFace )
  147. return;
  148. const CMapObjectList *pChildren = pEntity->GetChildren();
  149. FOR_EACH_OBJ( *pChildren, pos )
  150. {
  151. CMapOverlay *pOverlay = dynamic_cast<CMapOverlay*>( pChildren->Element(pos) );
  152. if ( pOverlay )
  153. {
  154. pOverlay->Basis_Init( pFace );
  155. pOverlay->Handles_Init( pFace );
  156. pOverlay->SideList_Init( pFace );
  157. pOverlay->SetOverlayType( OVERLAY_TYPE_GENERIC );
  158. pOverlay->SetLoaded( true );
  159. pOverlay->CalcBounds( true );
  160. }
  161. }
  162. }
  163. //-----------------------------------------------------------------------------
  164. //-----------------------------------------------------------------------------
  165. void CToolOverlay::OverlaySelection( CMapView3D *pView, UINT nFlags, const Vector2D &vPoint )
  166. {
  167. CMapObjectList aSelectionList;
  168. m_pDocument->GetSelection()->ClearHitList();
  169. // Find out how many (and what) map objects are under the point clicked on.
  170. HitInfo_t Objects[MAX_PICK_HITS];
  171. int nHits = pView->ObjectsAt( vPoint, Objects, sizeof( Objects ) / sizeof( Objects[0] ) );
  172. if ( nHits != 0 )
  173. {
  174. // We now have an array of pointers to CMapAtoms. Any that can be upcast to CMapClass objects?
  175. for ( int iHit = 0; iHit < nHits; ++iHit )
  176. {
  177. CMapClass *pMapClass = dynamic_cast<CMapClass*>( Objects[iHit].pObject );
  178. if ( pMapClass )
  179. {
  180. aSelectionList.AddToTail( pMapClass );
  181. }
  182. }
  183. }
  184. // Did we hit anything?
  185. if ( !aSelectionList.Count() )
  186. {
  187. m_pDocument->SelectFace( NULL, 0, scClear );
  188. m_pDocument->SelectObject( NULL, scClear|scSaveChanges );
  189. SetEmpty();
  190. return;
  191. }
  192. // Find overlays.
  193. bool bUpdateViews = false;
  194. SelectMode_t eSelectMode = m_pDocument->GetSelection()->GetMode();
  195. FOR_EACH_OBJ( aSelectionList, pos )
  196. {
  197. CMapClass *pObject = aSelectionList.Element( pos );
  198. CMapClass *pHitObject = pObject->PrepareSelection( eSelectMode );
  199. if ( pHitObject )
  200. {
  201. if ( pHitObject->IsMapClass( MAPCLASS_TYPE( CMapEntity ) ) )
  202. {
  203. const CMapObjectList *pChildren = pHitObject->GetChildren();
  204. FOR_EACH_OBJ( *pChildren, pos2 )
  205. {
  206. CMapOverlay *pOverlay = dynamic_cast<CMapOverlay*>( pChildren->Element(pos2) );
  207. if ( pOverlay )
  208. {
  209. m_pDocument->GetSelection()->AddHit( pHitObject );
  210. m_bEmpty = false;
  211. UINT cmd = scClear | scSelect | scSaveChanges;
  212. if (nFlags & MK_CONTROL)
  213. {
  214. cmd = scToggle;
  215. }
  216. m_pDocument->SelectObject( pHitObject, cmd );
  217. bUpdateViews = true;
  218. }
  219. }
  220. }
  221. }
  222. }
  223. // Update the views.
  224. if ( bUpdateViews )
  225. {
  226. m_pDocument->UpdateAllViews( MAPVIEW_UPDATE_OBJECTS );
  227. }
  228. }
  229. //-----------------------------------------------------------------------------
  230. //-----------------------------------------------------------------------------
  231. bool CToolOverlay::OnContextMenu2D( CMapView2D *pView, UINT nFlags, const Vector2D &vPoint )
  232. {
  233. static CMenu menu, menuOverlay;
  234. static bool bInit = false;
  235. if ( !bInit )
  236. {
  237. // Create the menu.
  238. menu.LoadMenu( IDR_POPUPS );
  239. menuOverlay.Attach( ::GetSubMenu( menu.m_hMenu, 6 ) );
  240. bInit = true;
  241. }
  242. if ( !pView->PointInClientRect(vPoint) )
  243. return false;
  244. if (!IsEmpty())
  245. {
  246. if ( HitTest( pView, vPoint, false ) )
  247. {
  248. CPoint ptScreen( vPoint.x,vPoint.y);
  249. pView->ClientToScreen(&ptScreen);
  250. menuOverlay.TrackPopupMenu( TPM_LEFTBUTTON | TPM_RIGHTBUTTON | TPM_LEFTALIGN, ptScreen.x, ptScreen.y, pView );
  251. }
  252. }
  253. return true;
  254. }
  255. //-----------------------------------------------------------------------------
  256. //-----------------------------------------------------------------------------
  257. bool CToolOverlay::UpdateTranslation( const Vector &vUpdate, UINT nFlags)
  258. {
  259. // if( m_bBoxSelecting )
  260. // return Box3D::UpdateTranslation( pt, nFlags );
  261. return true;
  262. }
  263. //-----------------------------------------------------------------------------
  264. //-----------------------------------------------------------------------------
  265. void CToolOverlay::HandlesReset( void )
  266. {
  267. // Go through selection list and reset overlay handles.
  268. const CMapObjectList *pSelection = m_pDocument->GetSelection()->GetList();
  269. for( int iSelection = 0; iSelection < pSelection->Count(); ++iSelection )
  270. {
  271. CMapClass *pMapClass = pSelection->Element( iSelection );
  272. if ( pMapClass && pMapClass->IsMapClass( MAPCLASS_TYPE( CMapEntity ) ) )
  273. {
  274. const CMapObjectList *pChildren = pMapClass->GetChildren();
  275. FOR_EACH_OBJ( *pChildren, pos )
  276. {
  277. CMapOverlay *pOverlay = dynamic_cast<CMapOverlay*>( pChildren->Element(pos) );
  278. if ( pOverlay )
  279. {
  280. pOverlay->HandlesReset();
  281. break;
  282. }
  283. }
  284. }
  285. }
  286. }
  287. //-----------------------------------------------------------------------------
  288. //-----------------------------------------------------------------------------
  289. bool CToolOverlay::HandleSelection( CMapView *pView, const Vector2D &vPoint )
  290. {
  291. // Reset the hit overlay.
  292. m_pActiveOverlay = NULL;
  293. // Go through selection list and test all overlay's handles and set the
  294. // "hit" overlay current.
  295. const CMapObjectList *pSelection = m_pDocument->GetSelection()->GetList();
  296. for ( int iSelection = 0; iSelection < pSelection->Count(); ++iSelection )
  297. {
  298. CMapClass *pMapClass = pSelection->Element( iSelection );
  299. if ( pMapClass && pMapClass->IsMapClass( MAPCLASS_TYPE( CMapEntity ) ) )
  300. {
  301. const CMapObjectList *pChildren = pMapClass->GetChildren();
  302. FOR_EACH_OBJ( *pChildren, pos )
  303. {
  304. CMapOverlay *pOverlay = dynamic_cast<CMapOverlay*>( pChildren->Element(pos) );
  305. if ( pOverlay && pOverlay->IsSelected() )
  306. {
  307. if ( pOverlay->HandlesHitTest( pView, vPoint ) )
  308. {
  309. m_pActiveOverlay = pOverlay;
  310. break;
  311. }
  312. }
  313. }
  314. }
  315. }
  316. if ( !m_pActiveOverlay )
  317. return false;
  318. return true;
  319. }
  320. //-----------------------------------------------------------------------------
  321. //-----------------------------------------------------------------------------
  322. bool CToolOverlay::HandleSnap( CMapOverlay *pOverlay, Vector &vecHandlePt )
  323. {
  324. Vector vecTmp;
  325. for ( int i = 0; i < OVERLAY_HANDLES_COUNT; i++ )
  326. {
  327. pOverlay->GetHandlePos( i, vecTmp );
  328. vecTmp -= vecHandlePt;
  329. float flDist = vecTmp.Length();
  330. if ( flDist < OVERLAY_TOOL_SNAP_DISTANCE )
  331. {
  332. // Snap!
  333. pOverlay->GetHandlePos( i, vecHandlePt );
  334. return true;
  335. }
  336. }
  337. return false;
  338. }
  339. //-----------------------------------------------------------------------------
  340. //-----------------------------------------------------------------------------
  341. bool CToolOverlay::HandleInBBox( CMapOverlay *pOverlay, Vector const &vecHandlePt )
  342. {
  343. Vector vecMin, vecMax;
  344. pOverlay->GetCullBox( vecMin, vecMax );
  345. for ( int iAxis = 0; iAxis < 3; iAxis++ )
  346. {
  347. vecMin[iAxis] -= OVERLAY_TOOL_SNAP_DISTANCE;
  348. vecMax[iAxis] += OVERLAY_TOOL_SNAP_DISTANCE;
  349. if( ( vecHandlePt[iAxis] < vecMin[iAxis] ) || ( vecHandlePt[iAxis] > vecMax[iAxis] ) )
  350. return false;
  351. }
  352. return true;
  353. }
  354. //-----------------------------------------------------------------------------
  355. //-----------------------------------------------------------------------------
  356. void CToolOverlay::SnapHandle( Vector &vecHandlePt )
  357. {
  358. CMapWorld *pWorld = GetActiveWorld();
  359. if ( !pWorld )
  360. return;
  361. EnumChildrenPos_t posWorld;
  362. CMapClass *pChild = pWorld->GetFirstDescendent( posWorld );
  363. while ( pChild )
  364. {
  365. CMapEntity *pEntity = dynamic_cast<CMapEntity*>( pChild );
  366. if ( pEntity )
  367. {
  368. const CMapObjectList *pChildren = pEntity->GetChildren();
  369. FOR_EACH_OBJ( *pChildren, pos )
  370. {
  371. CMapOverlay *pOverlay = dynamic_cast<CMapOverlay*>( pChildren->Element(pos) );
  372. if ( pOverlay && pOverlay != m_pActiveOverlay && pOverlay->IsSelected() )
  373. {
  374. // Intersection test and attempt to snap
  375. if ( HandleInBBox( pOverlay, vecHandlePt ) )
  376. {
  377. if ( HandleSnap( pOverlay, vecHandlePt ) )
  378. return;
  379. }
  380. }
  381. }
  382. }
  383. pChild = pWorld->GetNextDescendent( posWorld );
  384. }
  385. }
  386. //-----------------------------------------------------------------------------
  387. //-----------------------------------------------------------------------------
  388. void CToolOverlay::OnDrag( Vector const &vecRayStart, Vector const &vecRayEnd, bool bShift )
  389. {
  390. // Get the current overlay.
  391. CMapOverlay *pOverlay = m_pActiveOverlay;
  392. if ( !pOverlay )
  393. return;
  394. // Get a list of faces and test for "impact."
  395. Vector vecImpact( 0.0f, 0.0f, 0.0f );
  396. Vector vecImpactNormal( 0.0f, 0.0f, 0.0f );
  397. CMapFace *pFace = NULL;
  398. int nFaceCount = pOverlay->GetFaceCount();
  399. int iFace;
  400. for ( iFace = 0; iFace < nFaceCount; iFace++ )
  401. {
  402. pFace = pOverlay->GetFace( iFace );
  403. if ( pFace )
  404. {
  405. if ( pFace->TraceLineInside( vecImpact, vecImpactNormal, vecRayStart, vecRayEnd ) )
  406. break;
  407. }
  408. }
  409. // Test for impact (face index = count mean no impact).
  410. if ( iFace == nFaceCount )
  411. return;
  412. if ( bShift )
  413. {
  414. SnapHandle( vecImpact );
  415. }
  416. // Pass the new handle position to the overlay.
  417. pOverlay->HandlesDragTo( vecImpact, pFace );
  418. m_pDocument->UpdateAllViews( MAPVIEW_UPDATE_ONLY_3D );
  419. }
  420. //-----------------------------------------------------------------------------
  421. //-----------------------------------------------------------------------------
  422. void CToolOverlay::PreDrag( void )
  423. {
  424. m_bDragging = true;
  425. SetupHandleDragUndo();
  426. }
  427. //-----------------------------------------------------------------------------
  428. // Purpose: Renders the cordon tool in the 3D view.
  429. //-----------------------------------------------------------------------------
  430. void CToolOverlay::RenderTool3D(CRender3D *pRender)
  431. {
  432. // TODO render tool handles here and not in overlay rendering code
  433. }
  434. //-----------------------------------------------------------------------------
  435. //-----------------------------------------------------------------------------
  436. void CToolOverlay::PostDrag( void )
  437. {
  438. if ( !m_bDragging )
  439. return;
  440. m_bDragging = false;
  441. // Get the current overlay.
  442. CMapOverlay *pOverlay = m_pActiveOverlay;
  443. if ( pOverlay )
  444. {
  445. pOverlay->DoClip();
  446. pOverlay->CenterEntity();
  447. pOverlay->PostUpdate( Notify_Changed );
  448. m_pDocument->UpdateAllViews( MAPVIEW_UPDATE_OBJECTS );
  449. }
  450. // Reset the overlay handles.
  451. HandlesReset();
  452. }
  453. //-----------------------------------------------------------------------------
  454. //-----------------------------------------------------------------------------
  455. void CToolOverlay::SetupHandleDragUndo( void )
  456. {
  457. // Get the current overlay.
  458. CMapOverlay *pOverlay = m_pActiveOverlay;
  459. if ( pOverlay )
  460. {
  461. CMapEntity *pEntity = ( CMapEntity* )pOverlay->GetParent();
  462. if ( pEntity )
  463. {
  464. // Setup for drag undo.
  465. GetHistory()->MarkUndoPosition( m_pDocument->GetSelection()->GetList(), "Drag Overlay Handle" );
  466. GetHistory()->Keep( ( CMapClass* )pEntity );
  467. }
  468. }
  469. }