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.

690 lines
17 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose: Rendering and mouse handling in the 2D view.
  4. //
  5. //===========================================================================//
  6. #include "stdafx.h"
  7. #include "hammer.h"
  8. #include "MapView2D.h"
  9. #include "MapView3D.h"
  10. #include "MapDoc.h"
  11. #include "Render2D.h"
  12. #include "ToolManager.h"
  13. #include "History.h"
  14. #include "TitleWnd.h"
  15. #include "mainfrm.h"
  16. #include "MapSolid.h"
  17. #include "ToolMorph.h" // FIXME: remove
  18. #include "MapWorld.h"
  19. #include "camera.h"
  20. #include "Manifest.h"
  21. #include "MapInstance.h"
  22. #include "Options.h"
  23. // memdbgon must be the last include file in a .cpp file!!!
  24. #include <tier0/memdbgon.h>
  25. extern bool g_bUpdateBones2D;
  26. static DrawType_t __eNextViewType = VIEW2D_XY;
  27. IMPLEMENT_DYNCREATE(CMapView2D, CMapView2DBase)
  28. BEGIN_MESSAGE_MAP(CMapView2D, CMapView2DBase)
  29. //{{AFX_MSG_MAP(CMapView2D)
  30. ON_WM_KEYDOWN()
  31. ON_COMMAND(ID_VIEW_2DXY, OnView2dxy)
  32. ON_COMMAND(ID_VIEW_2DYZ, OnView2dyz)
  33. ON_COMMAND(ID_VIEW_2DXZ, OnView2dxz)
  34. ON_COMMAND_EX(ID_TOOLS_ALIGNTOP, OnToolsAlign)
  35. ON_COMMAND_EX(ID_TOOLS_ALIGNBOTTOM, OnToolsAlign)
  36. ON_COMMAND_EX(ID_TOOLS_ALIGNLEFT, OnToolsAlign)
  37. ON_COMMAND_EX(ID_TOOLS_ALIGNRIGHT, OnToolsAlign)
  38. ON_COMMAND_EX(ID_FLIP_HORIZONTAL, OnFlip)
  39. ON_COMMAND_EX(ID_FLIP_VERTICAL, OnFlip)
  40. ON_UPDATE_COMMAND_UI(ID_FLIP_HORIZONTAL, OnUpdateEditSelection)
  41. ON_UPDATE_COMMAND_UI(ID_FLIP_VERTICAL, OnUpdateEditSelection)
  42. ON_UPDATE_COMMAND_UI(ID_TOOLS_ALIGNTOP, OnUpdateEditSelection)
  43. ON_UPDATE_COMMAND_UI(ID_TOOLS_ALIGNBOTTOM, OnUpdateEditSelection)
  44. ON_UPDATE_COMMAND_UI(ID_TOOLS_ALIGNLEFT, OnUpdateEditSelection)
  45. ON_UPDATE_COMMAND_UI(ID_TOOLS_ALIGNRIGHT, OnUpdateEditSelection)
  46. //}}AFX_MSG_MAP
  47. END_MESSAGE_MAP()
  48. //-----------------------------------------------------------------------------
  49. // Purpose: Allows for iteration of draw types in order.
  50. // Input : eDrawType - Current draw type.
  51. // Output : Returns the next draw type in the list: XY, YZ, XZ. List wraps.
  52. //-----------------------------------------------------------------------------
  53. static DrawType_t NextDrawType(DrawType_t eDrawType)
  54. {
  55. if (eDrawType == VIEW2D_XY)
  56. {
  57. return(VIEW2D_YZ);
  58. }
  59. if (eDrawType == VIEW2D_YZ)
  60. {
  61. return(VIEW2D_XZ);
  62. }
  63. return(VIEW2D_XY);
  64. }
  65. //-----------------------------------------------------------------------------
  66. // Purpose: Allows for iteration of draw types in reverse order.
  67. // Input : eDrawType - Current draw type.
  68. // Output : Returns the previous draw type in the list: XY, YZ, XZ. List wraps.
  69. //-----------------------------------------------------------------------------
  70. static DrawType_t PrevDrawType(DrawType_t eDrawType)
  71. {
  72. if (eDrawType == VIEW2D_XY)
  73. {
  74. return(VIEW2D_XZ);
  75. }
  76. if (eDrawType == VIEW2D_YZ)
  77. {
  78. return(VIEW2D_XY);
  79. }
  80. return(VIEW2D_YZ);
  81. }
  82. //-----------------------------------------------------------------------------
  83. // Purpose: Constructor. Initializes data members.
  84. // ---------------------------------------------------------------------------
  85. CMapView2D::CMapView2D(void)
  86. {
  87. //
  88. // Create next 2d view type.
  89. //
  90. __eNextViewType = NextDrawType(__eNextViewType);
  91. SetDrawType(__eNextViewType);
  92. m_bUpdateRenderObjects = true;
  93. m_bLastActiveView = false;
  94. }
  95. //-----------------------------------------------------------------------------
  96. // Purpose: Destructor. Frees dynamically allocated resources.
  97. //-----------------------------------------------------------------------------
  98. CMapView2D::~CMapView2D(void)
  99. {
  100. if ( GetMapDoc() )
  101. {
  102. GetMapDoc()->RemoveMRU(this);
  103. }
  104. }
  105. //-----------------------------------------------------------------------------
  106. // Purpose: First-time initialization of this view.
  107. //-----------------------------------------------------------------------------
  108. void CMapView2D::OnInitialUpdate(void)
  109. {
  110. // NOTE: This must occur becore OnInitialUpdate
  111. // Creates the title window
  112. CreateTitleWindow();
  113. // NOTE: This must occur becore OnInitialUpdate
  114. // Other initialization.
  115. SetDrawType( GetDrawType() );
  116. CMapView2DBase::OnInitialUpdate();
  117. // Add to doc's MRU list
  118. CMapDoc *pDoc = GetMapDoc();
  119. pDoc->SetMRU(this);
  120. }
  121. //-----------------------------------------------------------------------------
  122. // Purpose:
  123. // Input : *pRender -
  124. //-----------------------------------------------------------------------------
  125. void CMapView2D::DrawPointFile( CRender2D *pRender )
  126. {
  127. pRender->SetDrawColor( 255,0,0 );
  128. int nPFPoints = GetMapDoc()->m_PFPoints.Count();
  129. Vector* pPFPoints = GetMapDoc()->m_PFPoints.Base();
  130. pRender->MoveTo( pPFPoints[0] );
  131. for(int i = 1; i < nPFPoints; i++)
  132. {
  133. pRender->DrawLineTo( pPFPoints[i] );
  134. }
  135. }
  136. //-----------------------------------------------------------------------------
  137. // Called when the base class wants the render lists to be recomputed
  138. //-----------------------------------------------------------------------------
  139. void CMapView2D::OnRenderListDirty()
  140. {
  141. m_bUpdateRenderObjects = true;
  142. }
  143. //-----------------------------------------------------------------------------
  144. // Purpose: Sorts the object to be rendered into one of two groups: normal objects
  145. // and selected objects, so that selected objects can be rendered last.
  146. // Input : pObject -
  147. // pRenderList -
  148. // Output : Returns TRUE on success, FALSE on failure.
  149. //-----------------------------------------------------------------------------
  150. void CMapView2D::AddToRenderLists(CMapClass *pObject)
  151. {
  152. if ( !pObject->IsVisible() )
  153. return;
  154. // Don't render groups, render their children instead.
  155. if ( !pObject->IsGroup() )
  156. {
  157. if ( !pObject->IsVisible2D() )
  158. return;
  159. Vector vecMins, vecMaxs;
  160. pObject->GetCullBox( vecMins, vecMaxs );
  161. if ( IsValidBox( vecMins, vecMaxs ) )
  162. {
  163. // Make sure the object is in the update region.
  164. if ( !IsInClientView(vecMins, vecMaxs) )
  165. return;
  166. }
  167. m_RenderList.AddToTail(pObject);
  168. }
  169. // Recurse into children and add them.
  170. const CMapObjectList *pChildren = pObject->GetChildren();
  171. FOR_EACH_OBJ( *pChildren, pos )
  172. {
  173. AddToRenderLists(pChildren->Element(pos));
  174. }
  175. }
  176. //-----------------------------------------------------------------------------
  177. // Purpose:
  178. // Input : rectUpdate -
  179. //-----------------------------------------------------------------------------
  180. void CMapView2D::Render()
  181. {
  182. CMapDoc *pDoc = GetMapDoc();
  183. CMapWorld *pWorld = pDoc->GetMapWorld();
  184. CManifest *pManifest = pDoc->GetManifest();
  185. if ( pManifest )
  186. {
  187. pWorld = pManifest->GetManifestWorld();
  188. }
  189. GetRender()->StartRenderFrame();
  190. if ( Options.general.bRadiusCulling )
  191. {
  192. DrawCullingCircleHelper2D( GetRender() );
  193. }
  194. // Draw grid if enabled.
  195. if (pDoc->m_bShowGrid)
  196. {
  197. DrawGrid( GetRender(), axHorz, axVert, 0 );
  198. }
  199. //
  200. // Draw the world if we have one.
  201. //
  202. if (pWorld == NULL)
  203. return;
  204. // Traverse the entire world, sorting visible elements into two arrays:
  205. // Normal objects and selected objects, so that we can render the selected
  206. // objects last.
  207. //
  208. if ( m_bUpdateRenderObjects )
  209. {
  210. m_RenderList.RemoveAll();
  211. // fill render lists with visible objects
  212. AddToRenderLists( pWorld );
  213. g_bUpdateBones2D = true;
  214. }
  215. //
  216. // Render normal (nonselected) objects first
  217. //
  218. GetRender()->PrepareInstanceStencil();
  219. CUtlVector<CMapClass *> selectedObjects;
  220. CUtlVector<CMapClass *> helperObjects;
  221. for (int i = 0; i < m_RenderList.Count(); i++)
  222. {
  223. CMapClass *pObject = m_RenderList[i];
  224. if ( pObject->IsSelected() )
  225. {
  226. // render later
  227. if ( pObject->GetToolObject(0,false) )
  228. {
  229. helperObjects.AddToTail( pObject );
  230. }
  231. else
  232. {
  233. selectedObjects.AddToTail( pObject );
  234. }
  235. }
  236. else
  237. {
  238. // render now
  239. pObject->Render2D( GetRender() );
  240. }
  241. }
  242. //
  243. // Render selected objects in second batch, so they overdraw normal object
  244. //
  245. for (int i = 0; i < selectedObjects.Count(); i++)
  246. {
  247. selectedObjects[i]->Render2D( GetRender() );
  248. }
  249. GetRender()->DrawInstanceStencil();
  250. //
  251. // Draw pointfile if enabled.
  252. //
  253. if (pDoc->m_PFPoints.Count())
  254. {
  255. DrawPointFile( GetRender() );
  256. }
  257. pDoc->RenderDocument( GetRender() );
  258. m_bUpdateRenderObjects = false;
  259. g_bUpdateBones2D = false;
  260. // render all tools
  261. CBaseTool *pCurTool = m_pToolManager->GetActiveTool();
  262. // render active tool
  263. if ( pCurTool )
  264. {
  265. pCurTool->RenderTool2D( GetRender() );
  266. }
  267. // render map helpers at last
  268. for (int i = 0; i < helperObjects.Count(); i++)
  269. {
  270. helperObjects[i]->Render2D( GetRender() );
  271. }
  272. GetRender()->EndRenderFrame();
  273. }
  274. //-----------------------------------------------------------------------------
  275. // Purpose: this function will render an instance map at the specific offset and rotation
  276. // Input : pInstanceClass - the map class of the func_instance
  277. // pMapClass - the map class of the world spawn of the instance
  278. // InstanceOrigin - the translation offset
  279. // InstanceAngles - the axis rotation
  280. // Output : none
  281. //-----------------------------------------------------------------------------
  282. void CMapView2D::RenderInstance( CMapInstance *pInstanceClass, CMapClass *pMapClass, Vector &InstanceOrigin, QAngle &InstanceAngles )
  283. {
  284. if ( !pInstanceClass->IsInstanceVisible() )
  285. {
  286. return;
  287. }
  288. GetRender()->PushInstanceData( pInstanceClass, InstanceOrigin, InstanceAngles );
  289. RenderInstanceMapClass_r( pMapClass );
  290. GetRender()->PopInstanceData();
  291. }
  292. //-----------------------------------------------------------------------------
  293. // Purpose: this function will recursively render an instance and all of its children
  294. // Input : pObject - the object to be rendered
  295. // Output : none
  296. //-----------------------------------------------------------------------------
  297. void CMapView2D::RenderInstanceMapClass_r( CMapClass *pObject )
  298. {
  299. if ( !pObject->IsVisible() )
  300. {
  301. return;
  302. }
  303. // Don't render groups, render their children instead.
  304. if ( !pObject->IsGroup() )
  305. {
  306. if ( !pObject->IsVisible2D() )
  307. {
  308. return;
  309. }
  310. Vector vecMins, vecMaxs, vecExpandedMins, vecExpandedMaxs;
  311. pObject->GetCullBox( vecMins, vecMaxs );
  312. GetRender()->TransformInstanceAABB( vecMins, vecMaxs, vecExpandedMins, vecExpandedMaxs );
  313. if ( IsValidBox( vecExpandedMins, vecExpandedMaxs ) )
  314. {
  315. // Make sure the object is in the update region.
  316. if ( !IsInClientView( vecExpandedMins, vecExpandedMaxs ) )
  317. {
  318. return;
  319. }
  320. }
  321. pObject->Render2D( GetRender() );
  322. }
  323. // Recurse into children and add them.
  324. const CMapObjectList *pChildren = pObject->GetChildren();
  325. FOR_EACH_OBJ( *pChildren, pos )
  326. {
  327. RenderInstanceMapClass_r(pChildren->Element(pos));
  328. }
  329. }
  330. //-----------------------------------------------------------------------------
  331. // Purpose:
  332. // Input : m_DrawType -
  333. // bForceUpdate -
  334. //-----------------------------------------------------------------------------
  335. void CMapView2D::SetDrawType(DrawType_t drawType)
  336. {
  337. Vector vOldView;
  338. // reset old third axis to selection center level
  339. m_pCamera->GetViewPoint( vOldView );
  340. CMapDoc *pDoc = GetMapDoc();
  341. if ( pDoc && !pDoc->GetSelection()->IsEmpty() )
  342. {
  343. Vector vCenter;
  344. GetMapDoc()->GetSelection()->GetBoundsCenter( vCenter );
  345. vOldView[axThird] = vCenter[axThird];
  346. }
  347. else
  348. {
  349. vOldView[axThird] = 0;
  350. }
  351. switch (drawType)
  352. {
  353. case VIEW2D_XY:
  354. SetAxes(AXIS_X, FALSE, AXIS_Y, TRUE);
  355. if ( HasTitleWnd() )
  356. {
  357. GetTitleWnd()->SetTitle("top (x/y)");
  358. }
  359. break;
  360. case VIEW2D_YZ:
  361. SetAxes(AXIS_Y, FALSE, AXIS_Z, TRUE);
  362. if ( HasTitleWnd() )
  363. {
  364. GetTitleWnd()->SetTitle("front (y/z)");
  365. }
  366. break;
  367. case VIEW2D_XZ:
  368. SetAxes(AXIS_X, FALSE, AXIS_Z, TRUE);
  369. if ( HasTitleWnd() )
  370. {
  371. GetTitleWnd()->SetTitle("side (x/z)");
  372. }
  373. break;
  374. }
  375. m_eDrawType = drawType;
  376. m_pCamera->SetViewPoint( vOldView );
  377. UpdateClientView();
  378. if (m_bLastActiveView && GetMapDoc())
  379. {
  380. GetMapDoc()->UpdateTitle(this);
  381. }
  382. }
  383. //-----------------------------------------------------------------------------
  384. // Purpose:
  385. //-----------------------------------------------------------------------------
  386. void CMapView2D::OnView2dxy(void)
  387. {
  388. SetDrawType(VIEW2D_XY);
  389. }
  390. //-----------------------------------------------------------------------------
  391. // Purpose:
  392. //-----------------------------------------------------------------------------
  393. void CMapView2D::OnView2dyz(void)
  394. {
  395. SetDrawType(VIEW2D_YZ);
  396. }
  397. //-----------------------------------------------------------------------------
  398. // Purpose:
  399. //-----------------------------------------------------------------------------
  400. void CMapView2D::OnView2dxz(void)
  401. {
  402. SetDrawType(VIEW2D_XZ);
  403. }
  404. //-----------------------------------------------------------------------------
  405. // Purpose:
  406. // Input : bActivate -
  407. // pActivateView -
  408. // pDeactiveView -
  409. //-----------------------------------------------------------------------------
  410. void CMapView2D::ActivateView(bool bActivate)
  411. {
  412. CMapView2DBase::ActivateView( bActivate );
  413. if ( bActivate )
  414. {
  415. CMapDoc *pDoc = GetMapDoc();
  416. pDoc->SetMRU(this);
  417. // tell doc to update title
  418. m_bLastActiveView = true;
  419. }
  420. else
  421. {
  422. m_bLastActiveView = false;
  423. }
  424. }
  425. //-----------------------------------------------------------------------------
  426. // Purpose:
  427. // Input : nID -
  428. // Output : Returns TRUE on success, FALSE on failure.
  429. //-----------------------------------------------------------------------------
  430. BOOL CMapView2D::OnToolsAlign(UINT nID)
  431. {
  432. CMapDoc *pDoc = GetMapDoc();
  433. CSelection *pSelection = pDoc->GetSelection();
  434. const CMapObjectList *pSelList = pSelection->GetList();
  435. GetHistory()->MarkUndoPosition(pSelList, "Align");
  436. GetHistory()->Keep(pSelList);
  437. // convert nID into the appropriate ID_TOOLS_ALIGNxxx define
  438. // taking into consideration the orientation of the axes
  439. if(nID == ID_TOOLS_ALIGNTOP && bInvertVert)
  440. nID = ID_TOOLS_ALIGNBOTTOM;
  441. else if(nID == ID_TOOLS_ALIGNBOTTOM && bInvertVert)
  442. nID = ID_TOOLS_ALIGNTOP;
  443. else if(nID == ID_TOOLS_ALIGNLEFT && bInvertHorz)
  444. nID = ID_TOOLS_ALIGNRIGHT;
  445. else if(nID == ID_TOOLS_ALIGNRIGHT && bInvertHorz)
  446. nID = ID_TOOLS_ALIGNLEFT;
  447. // use boundbox of selection - move all objects to match extreme
  448. // side of all the objects
  449. BoundBox box;
  450. pSelection->GetBounds(box.bmins, box.bmaxs);
  451. Vector ptMove( 0, 0, 0 );
  452. for (int i = 0; i < pSelList->Count(); i++)
  453. {
  454. CMapClass *pObject = pSelList->Element(i);
  455. Vector vecMins;
  456. Vector vecMaxs;
  457. pObject->GetRender2DBox(vecMins, vecMaxs);
  458. // align top
  459. if (nID == ID_TOOLS_ALIGNTOP)
  460. {
  461. ptMove[axVert] = box.bmins[axVert] - vecMins[axVert];
  462. }
  463. else if (nID == ID_TOOLS_ALIGNBOTTOM)
  464. {
  465. ptMove[axVert] = box.bmaxs[axVert] - vecMaxs[axVert];
  466. }
  467. else if (nID == ID_TOOLS_ALIGNLEFT)
  468. {
  469. ptMove[axHorz] = box.bmins[axHorz] - vecMins[axHorz];
  470. }
  471. else if (nID == ID_TOOLS_ALIGNRIGHT)
  472. {
  473. ptMove[axHorz] = box.bmaxs[axHorz] - vecMaxs[axHorz];
  474. }
  475. pObject->TransMove(ptMove);
  476. }
  477. pDoc->SetModifiedFlag();
  478. return TRUE;
  479. }
  480. //-----------------------------------------------------------------------------
  481. // Purpose: Flips the selection horizontally or vertically (with respect to the
  482. // view orientation.
  483. // Input : nID -
  484. // Output : Returns TRUE on success, FALSE on failure.
  485. //-----------------------------------------------------------------------------
  486. BOOL CMapView2D::OnFlip(UINT nID)
  487. {
  488. CMapDoc *pDoc = GetMapDoc();
  489. CSelection *pSelection = pDoc->GetSelection();
  490. const CMapObjectList *pSelList = pSelection->GetList();
  491. if ( pSelection->IsEmpty() )
  492. {
  493. return TRUE; // no selection
  494. }
  495. // flip objects from center of selection
  496. Vector ptRef, vScale(1,1,1);
  497. pSelection->GetBoundsCenter(ptRef);
  498. // never about this axis:
  499. if (nID == ID_FLIP_HORIZONTAL)
  500. {
  501. vScale[axHorz] = -1;
  502. }
  503. else if (nID == ID_FLIP_VERTICAL)
  504. {
  505. vScale[axVert] = -1;
  506. }
  507. GetHistory()->MarkUndoPosition( pSelList, "Flip Objects");
  508. GetHistory()->Keep(pSelList);
  509. // do flip
  510. for (int i = 0; i < pSelList->Count(); i++)
  511. {
  512. CMapClass *pObject = pSelList->Element(i);
  513. pObject->TransScale(ptRef,vScale);
  514. }
  515. pDoc->SetModifiedFlag();
  516. return TRUE;
  517. }
  518. //-----------------------------------------------------------------------------
  519. // Purpose: Manages the state of the Copy menu item.
  520. //-----------------------------------------------------------------------------
  521. void CMapView2D::OnUpdateEditSelection(CCmdUI *pCmdUI)
  522. {
  523. pCmdUI->Enable((!GetMapDoc()->GetSelection()->IsEmpty()) &&
  524. (m_pToolManager->GetActiveToolID() != TOOL_FACEEDIT_MATERIAL) &&
  525. !GetMainWnd()->IsShellSessionActive());
  526. }
  527. void CMapView2D::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags)
  528. {
  529. if (nChar == VK_TAB)
  530. {
  531. // swicth to next draw type
  532. SetDrawType( NextDrawType( m_eDrawType ) );
  533. return;
  534. }
  535. CMapView2DBase::OnKeyDown( nChar, nRepCnt, nFlags );
  536. }
  537. void CMapView2D::DrawCullingCircleHelper2D( CRender2D *pRender )
  538. {
  539. CMapDoc *pDoc = GetMapDoc();
  540. POSITION viewpos = pDoc->GetFirstViewPosition();
  541. while ( viewpos )
  542. {
  543. CMapView3D *pView = dynamic_cast<CMapView3D*>( pDoc->GetNextView( viewpos ) );
  544. if ( pView )
  545. {
  546. CCamera *pCamera = pView->GetCamera();
  547. Vector cameraPos;
  548. pCamera->GetViewPoint( cameraPos );
  549. int iClipDist = (int)pCamera->GetFarClip();
  550. pRender->SetDrawColor( 255, 0, 0 );
  551. pRender->DrawCircle( cameraPos, iClipDist );
  552. }
  553. }
  554. }