Counter Strike : Global Offensive Source Code
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.

714 lines
22 KiB

  1. //===== Copyright � 1996-2005, Valve Corporation, All rights reserved. ======//
  2. //
  3. // Purpose: Rendering and mouse handling in the logical view.
  4. //
  5. //===========================================================================//
  6. #include "stdafx.h"
  7. #include "MapViewLogical.h"
  8. #include "Render2D.h"
  9. #include "MapWorld.h"
  10. #include "TitleWnd.h"
  11. #include "MapDoc.h"
  12. #include "ToolManager.h"
  13. #include "history.h"
  14. // memdbgon must be the last include file in a .cpp file!!!
  15. #include <tier0/memdbgon.h>
  16. IMPLEMENT_DYNCREATE(CMapViewLogical, CMapView2DBase)
  17. BEGIN_MESSAGE_MAP(CMapViewLogical, CMapView2DBase)
  18. //{{AFX_MSG_MAP(CMapViewLogical)
  19. ON_WM_TIMER()
  20. //}}AFX_MSG_MAP
  21. END_MESSAGE_MAP()
  22. //-----------------------------------------------------------------------------
  23. // Purpose: Logical View Look and feel constants
  24. //-----------------------------------------------------------------------------
  25. #define LOGICAL_CONN_VERT_SPACING 100
  26. #define LOGICAL_CONN_HORIZ_SPACING 20
  27. #define LOGICAL_CONN_SPREAD_DIST 50
  28. #define LOGICAL_CONN_TEXT_LENGTH 450
  29. #define LOGICAL_CONN_CROSS_SIZE 30
  30. #define LOGICAL_CONN_MULTI_CIRCLE_RADIUS 15
  31. // Broken connection blinking interval (in ms)
  32. #define TIMER_BLINK_INTERVAL 512
  33. // Unselected and selected color values for the connections palette against the background color
  34. #define DARK 112
  35. #define MID 144
  36. #define BACKGROUND 168
  37. #define LITE 224
  38. #define BRITE 255
  39. #define LOGICAL_CONN_COLOR_COUNT 7
  40. #define LOGICAL_CONN_SELECTION_STATES 2
  41. static color32 s_pWireColors[LOGICAL_CONN_COLOR_COUNT][LOGICAL_CONN_SELECTION_STATES] =
  42. {
  43. { { MID, MID, 0, 255 }, /* Mid Yellow */ { BRITE, BRITE, 0, 255 }, /* Bright Yellow */ },
  44. { { MID, DARK, 0, 255 }, /* Dark Orange */ { BRITE, MID, 0, 255 }, /* Bright Orange */ },
  45. { { 0, DARK, 0, 255 }, /* Dark Green */ { 0, BRITE, 0, 255 }, /* Bright Green */ },
  46. { { 0, MID, MID, 255 }, /* Mid Cyan */ { 0, BRITE, BRITE, 255 }, /* Bright Cyan */ },
  47. { { 0, 0, MID, 255 }, /* Mid Blue */ { 0, MID, BRITE, 255 }, /* Bright Baby Blue */ },
  48. { { MID, 0, MID, 255 }, /* Mid Magenta */ { BRITE, 0, BRITE, 255 }, /* Bright Magenta */ },
  49. { { DARK, DARK, DARK, 255 }, /* Dark Gray */ { BRITE, BRITE, BRITE, 255 }, /* Bright White */ },
  50. };
  51. static color32 s_pBrokenWireColor[LOGICAL_CONN_SELECTION_STATES] =
  52. {
  53. { DARK, 0, 0, 255 }, /* Dark Red */ { BRITE, 0, 0, 255 }, /* Bright Red */
  54. };
  55. //-----------------------------------------------------------------------------
  56. // Purpose: Constructor. Initializes data members.
  57. // ---------------------------------------------------------------------------
  58. CMapViewLogical::CMapViewLogical(void) : m_RenderDict( 0, 1024, DefLessFunc( CMapClass* ) )
  59. {
  60. m_bUpdateRenderObjects = true;
  61. SetAxes(AXIS_X, FALSE, AXIS_Y, TRUE);
  62. SetDrawType( VIEW_LOGICAL );
  63. }
  64. //-----------------------------------------------------------------------------
  65. // Purpose: Destructor. Frees dynamically allocated resources.
  66. //-----------------------------------------------------------------------------
  67. CMapViewLogical::~CMapViewLogical(void)
  68. {
  69. }
  70. //-----------------------------------------------------------------------------
  71. // Purpose: First-time initialization of this view.
  72. //-----------------------------------------------------------------------------
  73. void CMapViewLogical::OnInitialUpdate(void)
  74. {
  75. CreateTitleWindow();
  76. GetTitleWnd()->SetTitle("Logical");
  77. SetZoom(0); // Zoom out as far as possible.
  78. UpdateClientView();
  79. CMapView2DBase::OnInitialUpdate();
  80. // FIXME: Hardcoded light gray background - should be from a new "Logical View" options settings dialog
  81. m_ClearColor.SetColor( BACKGROUND, BACKGROUND, BACKGROUND, 255 );
  82. m_clrGrid.SetColor( MID, MID, MID, 255 );
  83. }
  84. //-----------------------------------------------------------------------------
  85. // Purpose:
  86. // Input : point - Point in client coordinates.
  87. // bMakeFirst -
  88. // Output : Returns true on success, false on failure.
  89. //-----------------------------------------------------------------------------
  90. bool CMapViewLogical::SelectAtCascading( const Vector2D &ptClient, bool bMakeFirst )
  91. {
  92. CMapDoc *pDoc = GetMapDoc();
  93. CSelection *pSelection = pDoc->GetSelection();
  94. pSelection->ClearHitList();
  95. GetHistory()->MarkUndoPosition(pSelection->GetList(), "Selection");
  96. //
  97. // Check all the objects in the world for a hit at this point.
  98. //
  99. HitInfo_t HitData[MAX_PICK_HITS];
  100. int nHits = ObjectsAt(ptClient, HitData, MAX_PICK_HITS);
  101. // If there were no hits at the given point, clear selection.
  102. if ( nHits == 0 )
  103. {
  104. if (bMakeFirst)
  105. {
  106. pDoc->SelectFace(NULL, 0, scClear|scSaveChanges);
  107. pDoc->SelectObject(NULL, scClear|scSaveChanges);
  108. }
  109. return false;
  110. }
  111. SelectMode_t eSelectMode = pSelection->GetMode();
  112. for ( int i=0; i<nHits; ++i )
  113. {
  114. CMapClass *pSelObject = HitData[i].pObject->PrepareSelection( eSelectMode );
  115. if ( !pSelObject )
  116. continue;
  117. pSelection->AddHit( pSelObject );
  118. }
  119. //
  120. // Select a single object.
  121. //
  122. if ( bMakeFirst )
  123. {
  124. pDoc->SelectFace( NULL, 0, scClear|scSaveChanges );
  125. pDoc->SelectObject( NULL, scClear|scSaveChanges );
  126. }
  127. pSelection->SetCurrentHit( hitFirst, true );
  128. return true;
  129. }
  130. //-----------------------------------------------------------------------------
  131. // Base class calls this when render lists need rebuilding
  132. //-----------------------------------------------------------------------------
  133. void CMapViewLogical::OnRenderListDirty()
  134. {
  135. m_bUpdateRenderObjects = true;
  136. }
  137. //-----------------------------------------------------------------------------
  138. // Purpose: Builds up list of mapclasses to render
  139. //-----------------------------------------------------------------------------
  140. void CMapViewLogical::AddToRenderLists( CMapClass *pObject )
  141. {
  142. #if _DEBUG && 0
  143. CMapEntity *pEntity = dynamic_cast<CMapEntity *>(pObject);
  144. if (pEntity)
  145. {
  146. LPCTSTR pszTargetName = pEntity->GetKeyValue("targetname");
  147. if ( pszTargetName && !strcmp(pszTargetName, "relay_cancelVCDs") )
  148. {
  149. // Set breakpoint here for debugging this entity's visiblity
  150. int foo = 0;
  151. }
  152. }
  153. #endif
  154. if ( !pObject->IsVisibleLogical() )
  155. return;
  156. // Don't render groups, render their children instead.
  157. if ( !pObject->IsGroup() && pObject->IsLogical() )
  158. {
  159. Vector2D vecMins, vecMaxs;
  160. pObject->GetRenderLogicalBox( vecMins, vecMaxs );
  161. // Always paint all the entities to ensure that any inter-entity connections are visible
  162. // if ( !IsValidBox( vecMins, vecMaxs ) || IsInClientView( vecMins, vecMaxs ) )
  163. {
  164. // Make sure the object is in the update region.
  165. m_RenderList.AddToTail( pObject );
  166. m_ConnectionList.AddToTail( pObject );
  167. m_RenderDict.Insert( pObject );
  168. }
  169. }
  170. // Recurse into children and add them.
  171. const CMapObjectList *pChildren = pObject->GetChildren();
  172. FOR_EACH_OBJ( *pChildren, pos )
  173. {
  174. AddToRenderLists( (CUtlReference< CMapClass >)pChildren->Element(pos) );
  175. }
  176. }
  177. //-----------------------------------------------------------------------------
  178. // Purpose: Builds up list of mapclasses to render
  179. //-----------------------------------------------------------------------------
  180. void CMapViewLogical::PopulateConnectionList( )
  181. {
  182. while ( m_ConnectionUpdate.Count() )
  183. {
  184. CMapClass *pObject;
  185. m_ConnectionUpdate.Pop( pObject );
  186. if ( !pObject->IsVisibleLogical() )
  187. continue;
  188. // Don't render groups, render their children instead.
  189. if ( !pObject->IsGroup() && pObject->IsLogical() )
  190. {
  191. // Don't add it if it's visible already
  192. if ( m_RenderDict.Find( pObject ) == m_RenderDict.InvalidIndex() )
  193. {
  194. CEditGameClass *pClass = dynamic_cast< CEditGameClass * >( pObject );
  195. if ( pClass )
  196. {
  197. int nCount = pClass->Connections_GetCount();
  198. for ( int i = 0; i < nCount; ++i )
  199. {
  200. CEntityConnection *pConn = pClass->Connections_Get( i );
  201. // Find the input entity associated with this connection
  202. CMapEntityList *pEntityList = pConn->GetTargetEntityList();
  203. int j;
  204. int nInputCount = pEntityList->Count();
  205. for ( j = 0; j < nInputCount; ++j )
  206. {
  207. CMapEntity *pEntity = pEntityList->Element(j);
  208. if ( !pEntity )
  209. continue;
  210. if ( m_RenderDict.Find( pEntity ) != m_RenderDict.InvalidIndex() )
  211. {
  212. m_ConnectionList.AddToTail( pObject );
  213. break;
  214. }
  215. }
  216. if ( j != nInputCount )
  217. break;
  218. }
  219. }
  220. }
  221. }
  222. // Recurse into children and add them.
  223. const CMapObjectList *pChildren = pObject->GetChildren();
  224. FOR_EACH_OBJ( *pChildren, pos )
  225. {
  226. m_ConnectionUpdate.Push( (CUtlReference< CMapClass >)pChildren->Element(pos) );
  227. }
  228. }
  229. }
  230. //-----------------------------------------------------------------------------
  231. // Purpose:
  232. // Input : nIDEvent -
  233. //-----------------------------------------------------------------------------
  234. void CMapViewLogical::OnTimer(UINT nIDEvent)
  235. {
  236. if ( nIDEvent == TIMER_CONNECTIONUPDATE )
  237. {
  238. // Make sure we don't blink too fast
  239. static unsigned int nLastUpdate = 0;
  240. if ( GetTickCount() >= (nLastUpdate+TIMER_BLINK_INTERVAL/2) )
  241. {
  242. nLastUpdate = GetTickCount();
  243. UpdateView( 0 ); // Force the view to redraw for blinking errors
  244. }
  245. }
  246. else
  247. CView::OnTimer(nIDEvent);
  248. }
  249. const color32 & CMapViewLogical::GetWireColor(const char *pszName, const bool bSelected, const bool bError, const bool bAnySelected)
  250. {
  251. // Select the connecting color based on the string passed in
  252. // (using OutputName for instance, gives varying "wire colors" by entity output type).
  253. Assert( LOGICAL_CONN_COLOR_COUNT == (sizeof(s_pWireColors) / sizeof(color32) ) / LOGICAL_CONN_SELECTION_STATES );
  254. if ( !bError )
  255. {
  256. int nIndex = 0;
  257. // Has the named passed in
  258. while ( *pszName )
  259. nIndex += *pszName++;
  260. // Index based on the number of colors available
  261. nIndex %= LOGICAL_CONN_COLOR_COUNT;
  262. // Only blink non-errors if the drawing object is selected
  263. // bool bBlinkingState = bSelected ? (GetTickCount() / TIMER_BLINK_INTERVAL) & 1 : 0;
  264. return s_pWireColors[nIndex][bSelected];
  265. }
  266. else
  267. {
  268. // Only blink errors if nothing is selected, or this is selected
  269. bool bBlinkingState = bSelected || !bAnySelected ? (GetTickCount() / TIMER_BLINK_INTERVAL) & 1 : bSelected;
  270. return s_pBrokenWireColor[bBlinkingState];
  271. }
  272. }
  273. //-----------------------------------------------------------------------------
  274. // Draws a wire from a particular point to a target
  275. //-----------------------------------------------------------------------------
  276. #define BACKWARD_WIRE_OVERSHOOT 50
  277. #define BACKWARD_WIRE_Y_DISTANCE 150
  278. void CMapViewLogical::DrawConnectingWire( float x, float y, CMapEntity *pSource, CEntityConnection *pConnection, CMapEntity *pTarget )
  279. {
  280. CRender2D *pRender = GetRender();
  281. // FIXME: Deal with bad input type
  282. Vector2D vecEndPosition, vecConnector;
  283. pTarget->GetLogicalConnectionPosition( LOGICAL_CONNECTION_INPUT, vecConnector );
  284. vecEndPosition = vecConnector;
  285. int nInputs = pTarget->Upstream_GetCount();
  286. // Compensate for multiple inputs -- fan-in the connections from the various pSource entities
  287. BOOL bFound = false;
  288. if ( nInputs )
  289. {
  290. int nInput;
  291. for ( nInput = 0; nInput < nInputs; nInput++ )
  292. {
  293. CEntityConnection *pInputConnection = pTarget->Upstream_Get(nInput);
  294. if ( pInputConnection )
  295. {
  296. if ( pInputConnection == pConnection )
  297. {
  298. bFound = true;
  299. break;
  300. }
  301. }
  302. }
  303. if (bFound)
  304. {
  305. vecEndPosition.x -= LOGICAL_CONN_SPREAD_DIST;
  306. vecEndPosition.y += ( (nInputs - 1) * LOGICAL_CONN_VERT_SPACING / 2 ) / 2 - (nInput * LOGICAL_CONN_VERT_SPACING / 2);
  307. pRender->MoveTo( Vector( vecEndPosition.x, vecEndPosition.y, 0.0f) );
  308. pRender->DrawLineTo( Vector( vecConnector.x, vecConnector.y, 0.0f) );
  309. }
  310. else
  311. {
  312. Assert(0);
  313. }
  314. }
  315. pRender->MoveTo( Vector( x, y, 0.0f ) );
  316. if ( x < vecEndPosition.x )
  317. {
  318. // Do direct connection
  319. pRender->DrawLineTo( Vector( x, vecEndPosition.y, 0.0f ) );
  320. pRender->DrawLineTo( Vector( vecEndPosition.x, vecEndPosition.y, 0.0f ) );
  321. return;
  322. }
  323. Vector2D vecTargetMins, vecTargetMaxs;
  324. pTarget->GetRenderLogicalBox( vecTargetMins, vecTargetMaxs );
  325. vecTargetMins.y -= BACKWARD_WIRE_Y_DISTANCE;
  326. vecTargetMaxs.y += BACKWARD_WIRE_Y_DISTANCE;
  327. float flHalfY = ( y + vecEndPosition.y ) * 0.5f;
  328. if ( flHalfY > vecTargetMins.y && flHalfY < vecTargetMaxs.y )
  329. {
  330. flHalfY = ( flHalfY < vecEndPosition.y ) ? vecTargetMins.y : vecTargetMaxs.y;
  331. }
  332. pRender->DrawLineTo( Vector( x, flHalfY, 0.0f ) );
  333. pRender->DrawLineTo( Vector( vecEndPosition.x - BACKWARD_WIRE_OVERSHOOT, flHalfY, 0.0f ) );
  334. pRender->DrawLineTo( Vector( vecEndPosition.x - BACKWARD_WIRE_OVERSHOOT, vecEndPosition.y, 0.0f ) );
  335. pRender->DrawLineTo( Vector( vecEndPosition.x, vecEndPosition.y, 0.0f ) );
  336. }
  337. void CMapViewLogical::RenderConnections(const bool bDrawSelected, const bool bAnySelected)
  338. {
  339. Vector2D pt, pt2;
  340. WorldToClient( pt, Vector( 0.0f, 0.0f, 0.0f ) );
  341. WorldToClient( pt2, Vector( 0.0f, LOGICAL_CONN_VERT_SPACING, 0.0f ) );
  342. int nCount = m_ConnectionList.Count();
  343. for ( int i = 0; i < nCount ; ++i )
  344. {
  345. CMapEntity *pMapClass = dynamic_cast<CMapEntity*>( m_ConnectionList[i] );
  346. if ( !pMapClass )
  347. continue;
  348. CEditGameClass *pEditClass = dynamic_cast<CEditGameClass*>( pMapClass );
  349. if ( !pEditClass )
  350. continue;
  351. int nConnectionCount = pEditClass->Connections_GetCount();
  352. if ( nConnectionCount == 0 )
  353. continue;
  354. Vector2D vecStartPosition;
  355. pMapClass->GetLogicalConnectionPosition( LOGICAL_CONNECTION_OUTPUT, vecStartPosition );
  356. CRender2D *pRender = GetRender();
  357. float x = vecStartPosition.x + LOGICAL_CONN_SPREAD_DIST + LOGICAL_CONN_TEXT_LENGTH;
  358. float y = vecStartPosition.y + ( nConnectionCount - 1 ) * LOGICAL_CONN_VERT_SPACING / 2;
  359. for ( int j = 0; j < nConnectionCount; ++j )
  360. {
  361. CEntityConnection *pConn = pEditClass->Connections_Get( j );
  362. // Find the input entity associated with this connection
  363. CMapEntityList *pEntityList = pConn->GetTargetEntityList();
  364. int nInputCount = pEntityList->Count();
  365. bool bBadInput = !MapEntityList_HasInput( pEntityList, pConn->GetInputName() );
  366. bool bBadConnection = (nInputCount == 0);
  367. // Stop drawing entity connection text once the entity itself gets too small.
  368. bool bDrawOutput = ( fabs( pt.y - pt2.y ) >= 16 );
  369. bool bDrawDelay = ( fabs( pt.y - pt2.y ) >= 20 );
  370. bool bDrawInput = ( fabs( pt.y - pt2.y ) >= 24 );
  371. bool bDrawTarget = ( fabs( pt.y - pt2.y ) >= 28 );
  372. bool bEntitySelected = ( pMapClass->GetSelectionState() != SELECT_NONE );
  373. bool bInputSelected = false;
  374. for ( int k = 0; k < nInputCount; ++k )
  375. {
  376. if ( pEntityList->Element( k )->GetSelectionState() != SELECT_NONE )
  377. bInputSelected = true;
  378. // Make sure all the connected entities are all visible
  379. if ( pEntityList->Element( k )->IsVisibleLogical() == false )
  380. bBadConnection = true;
  381. }
  382. // Make sure we only draw the selected OR unselected connections as requested
  383. if ( bDrawSelected == (bEntitySelected || bInputSelected) )
  384. {
  385. color32 c = GetWireColor( pConn->GetOutputName(),
  386. bEntitySelected || bInputSelected,
  387. bBadConnection || bBadInput,
  388. bAnySelected );
  389. if ( bDrawDelay || bDrawOutput || bDrawInput || bDrawTarget )
  390. {
  391. char pBuf[1024];
  392. pRender->SetTextColor( c.r, c.g, c.b );
  393. int nChars = 0;
  394. if ( bDrawOutput )
  395. nChars += Q_snprintf( pBuf+nChars, sizeof(pBuf), "%s", pConn->GetOutputName() );
  396. if ( bDrawDelay )
  397. nChars += Q_snprintf( pBuf+nChars, sizeof(pBuf), "(%.2f)", pConn->GetDelay() );
  398. if ( nChars )
  399. pRender->DrawText( pBuf, Vector2D( vecStartPosition.x + LOGICAL_CONN_SPREAD_DIST, y ), 2, 1, CRender2D::TEXT_JUSTIFY_TOP | CRender2D::TEXT_JUSTIFY_RIGHT );
  400. nChars = 0;
  401. if ( bDrawInput )
  402. nChars += Q_snprintf( pBuf+nChars, sizeof(pBuf), "%s", pConn->GetInputName() );
  403. if ( bDrawTarget )
  404. nChars += Q_snprintf( pBuf+nChars, sizeof(pBuf), "[%s] ", pConn->GetTargetName() );
  405. if ( nChars )
  406. pRender->DrawText( pBuf, Vector2D( vecStartPosition.x + LOGICAL_CONN_SPREAD_DIST, y ), 2, -1, CRender2D::TEXT_JUSTIFY_BOTTOM | CRender2D::TEXT_JUSTIFY_RIGHT );
  407. }
  408. pRender->SetDrawColor( c.r, c.g, c.b );
  409. pRender->MoveTo( Vector( vecStartPosition.x, vecStartPosition.y, 0.0f ) );
  410. pRender->DrawLineTo( Vector( vecStartPosition.x + LOGICAL_CONN_SPREAD_DIST, y, 0.0f ) );
  411. pRender->DrawLineTo( Vector( x, y, 0.0f ) );
  412. if ( bBadConnection )
  413. {
  414. // Draw an X for a bogus connection.
  415. pRender->MoveTo( Vector( x - LOGICAL_CONN_CROSS_SIZE, y - LOGICAL_CONN_CROSS_SIZE, 0.0f ) );
  416. pRender->DrawLineTo( Vector( x + LOGICAL_CONN_CROSS_SIZE, y + LOGICAL_CONN_CROSS_SIZE, 0.0f ) );
  417. pRender->MoveTo( Vector( x - LOGICAL_CONN_CROSS_SIZE, y + LOGICAL_CONN_CROSS_SIZE, 0.0f ) );
  418. pRender->DrawLineTo( Vector( x + LOGICAL_CONN_CROSS_SIZE, y - LOGICAL_CONN_CROSS_SIZE, 0.0f ) );
  419. }
  420. else if ( nInputCount == 1 )
  421. {
  422. DrawConnectingWire( x, y, pMapClass, pConn, pEntityList->Element( 0 ) );
  423. }
  424. else
  425. {
  426. // Draw a circle for multiple connections
  427. pRender->DrawCircle( Vector( x + LOGICAL_CONN_MULTI_CIRCLE_RADIUS, y, 0.0f ), LOGICAL_CONN_MULTI_CIRCLE_RADIUS );
  428. float mx = x + LOGICAL_CONN_SPREAD_DIST;
  429. float my = y + ( nInputCount / 2 ) * LOGICAL_CONN_VERT_SPACING/2;
  430. Vector vecStart( x + LOGICAL_CONN_MULTI_CIRCLE_RADIUS, y, 0.0f );
  431. Vector vecDelta;
  432. for ( int k = 0; k < nInputCount; ++k )
  433. {
  434. // bBadInput = false; // This should be based on whether downstream entity has the specificied named input
  435. bInputSelected = ( pEntityList->Element( k )->GetSelectionState() != SELECT_NONE );
  436. color32 c = GetWireColor( pConn->GetOutputName(),
  437. bEntitySelected || bInputSelected,
  438. bBadInput,
  439. bAnySelected );
  440. pRender->SetDrawColor( c.r, c.g, c.b );
  441. Vector vecEnd( mx, my, 0.0f );
  442. Vector vecDelta;
  443. VectorSubtract( vecEnd, vecStart, vecDelta );
  444. VectorNormalize( vecDelta );
  445. vecDelta *= LOGICAL_CONN_MULTI_CIRCLE_RADIUS;
  446. vecDelta += vecStart;
  447. pRender->MoveTo( vecDelta );
  448. pRender->DrawLineTo( Vector( mx, my, 0.0f ) );
  449. DrawConnectingWire( mx, my, pMapClass, pConn, pEntityList->Element( k ) );
  450. mx += LOGICAL_CONN_HORIZ_SPACING;
  451. my -= LOGICAL_CONN_VERT_SPACING/2;
  452. }
  453. }
  454. }
  455. x += LOGICAL_CONN_HORIZ_SPACING * (nInputCount+1) + 2*LOGICAL_CONN_MULTI_CIRCLE_RADIUS;
  456. y -= LOGICAL_CONN_VERT_SPACING;
  457. }
  458. }
  459. }
  460. //-----------------------------------------------------------------------------
  461. // Purpose:
  462. //-----------------------------------------------------------------------------
  463. void CMapViewLogical::Render()
  464. {
  465. CMapDoc *pDoc = GetMapDoc();
  466. CMapWorld *pWorld = pDoc->GetMapWorld();
  467. GetRender()->StartRenderFrame( false );
  468. // Draw grid if enabled.
  469. if ( pDoc->m_bShowLogicalGrid )
  470. {
  471. DrawGridLogical( GetRender() );
  472. }
  473. // Draw the world if we have one.
  474. if (pWorld == NULL)
  475. return;
  476. // Traverse the entire world, sorting visible elements into two arrays:
  477. // Normal objects and selected objects, so that we can render the selected
  478. // objects last.
  479. if ( m_bUpdateRenderObjects )
  480. {
  481. m_bUpdateRenderObjects = false;
  482. m_RenderList.RemoveAll();
  483. m_RenderDict.RemoveAll();
  484. m_ConnectionList.RemoveAll();
  485. m_ConnectionUpdate.Clear();
  486. // fill render lists with visible objects
  487. AddToRenderLists( pWorld );
  488. // Add the connections too
  489. m_ConnectionUpdate.Push( pWorld );
  490. PopulateConnectionList();
  491. // Make sure we have a timer running to drive error animations
  492. SetTimer( TIMER_CONNECTIONUPDATE, TIMER_BLINK_INTERVAL, NULL);
  493. }
  494. // Assume we are blinking, unless something is selected.
  495. bool bAnySelected = FALSE;
  496. CUtlVector<CMapClass *> selectedObjects;
  497. CUtlVector<CMapClass *> helperObjects;
  498. CUtlVector<CMapClass *> unselectedObjects;
  499. // Render normal (nonselected) objects first
  500. for (int i = 0; i < m_RenderList.Count(); i++)
  501. {
  502. CMapClass *pObject = m_RenderList[i];
  503. if ( pObject->IsSelected() )
  504. {
  505. bAnySelected = TRUE;
  506. // render later
  507. if ( pObject->GetToolObject( 0, false ) )
  508. {
  509. helperObjects.AddToTail( pObject );
  510. }
  511. else
  512. {
  513. selectedObjects.AddToTail( pObject );
  514. }
  515. }
  516. else
  517. {
  518. unselectedObjects.AddToTail( pObject );
  519. }
  520. }
  521. // Render unselected connections first
  522. RenderConnections(false, bAnySelected);
  523. // render unselected objects next, on top of the connections
  524. for ( int j = 0; j < unselectedObjects.Count(); j++ )
  525. {
  526. unselectedObjects[j]->RenderLogical( GetRender() );
  527. }
  528. if ( bAnySelected )
  529. {
  530. // Render selected objects in second batch, so they overdraw normal object
  531. for (int i = 0; i < selectedObjects.Count(); i++)
  532. {
  533. selectedObjects[i]->RenderLogical( GetRender() );
  534. }
  535. // Render selected connections on top of everything else
  536. RenderConnections(true, bAnySelected);
  537. }
  538. // render all tools
  539. CBaseTool *pCurTool = pDoc->GetTools()->GetActiveTool();
  540. int nToolCount = pDoc->GetTools()->GetToolCount();
  541. for (int i = 0; i < nToolCount; i++)
  542. {
  543. CBaseTool *pTool = pDoc->GetTools()->GetTool(i);
  544. if ((pTool != NULL) && (pTool != pCurTool))
  545. {
  546. pTool->RenderToolLogical( GetRender() );
  547. }
  548. }
  549. // render active tool over all other tools
  550. if ( pCurTool )
  551. {
  552. pCurTool->RenderToolLogical( GetRender() );
  553. }
  554. // render map helpers at last
  555. for (int i = 0; i < helperObjects.Count(); i++)
  556. {
  557. helperObjects[i]->RenderLogical( GetRender() );
  558. }
  559. GetRender()->EndRenderFrame();
  560. }
  561. //-----------------------------------------------------------------------------
  562. // convert client view space to map world coordinates (2D versions for convenience)
  563. //-----------------------------------------------------------------------------
  564. void CMapViewLogical::WorldToClient( Vector2D &ptClient, const Vector2D &vWorld )
  565. {
  566. Vector vWorld3D( vWorld.x, vWorld.y, 0.0f );
  567. CMapView2DBase::WorldToClient( ptClient, vWorld3D );
  568. }
  569. void CMapViewLogical::ClientToWorld( Vector2D &vWorld, const Vector2D &vClient )
  570. {
  571. Vector vWorld3D;
  572. CMapView2DBase::ClientToWorld( vWorld3D, vClient );
  573. vWorld.x = vWorld3D.x;
  574. vWorld.y = vWorld3D.y;
  575. }
  576. void CMapViewLogical::WorldToClient( Vector2D &ptClient, const Vector &vWorld )
  577. {
  578. CMapView2DBase::WorldToClient( ptClient, vWorld );
  579. }
  580. void CMapViewLogical::ClientToWorld( Vector &vWorld, const Vector2D &vClient )
  581. {
  582. CMapView2DBase::ClientToWorld( vWorld, vClient );
  583. }