diff options
| author | FluorescentCIAAfricanAmerican <[email protected]> | 2020-04-22 12:56:21 -0400 |
|---|---|---|
| committer | FluorescentCIAAfricanAmerican <[email protected]> | 2020-04-22 12:56:21 -0400 |
| commit | 3bf9df6b2785fa6d951086978a3e66f49427166a (patch) | |
| tree | 2c0f1f0c63c4832882bc93814ebd2c2b1c6224e5 /hammer/mapviewlogical.cpp | |
| download | archived-source-engine-2018-hl2-src-master.tar.xz archived-source-engine-2018-hl2-src-master.zip | |
Diffstat (limited to 'hammer/mapviewlogical.cpp')
| -rw-r--r-- | hammer/mapviewlogical.cpp | 710 |
1 files changed, 710 insertions, 0 deletions
diff --git a/hammer/mapviewlogical.cpp b/hammer/mapviewlogical.cpp new file mode 100644 index 0000000..ca3a6e7 --- /dev/null +++ b/hammer/mapviewlogical.cpp @@ -0,0 +1,710 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: Rendering and mouse handling in the logical view. +// +//===========================================================================// + +#include "stdafx.h" +#include "MapViewLogical.h" +#include "Render2D.h" +#include "MapWorld.h" +#include "TitleWnd.h" +#include "MapDoc.h" +#include "ToolManager.h" +#include "history.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include <tier0/memdbgon.h> + + +IMPLEMENT_DYNCREATE(CMapViewLogical, CMapView2DBase) + + +BEGIN_MESSAGE_MAP(CMapViewLogical, CMapView2DBase) + //{{AFX_MSG_MAP(CMapViewLogical) + ON_WM_TIMER() + //}}AFX_MSG_MAP +END_MESSAGE_MAP() + + +//----------------------------------------------------------------------------- +// Purpose: Logical View Look and feel constants +//----------------------------------------------------------------------------- + +#define LOGICAL_CONN_VERT_SPACING 100 +#define LOGICAL_CONN_HORIZ_SPACING 20 +#define LOGICAL_CONN_SPREAD_DIST 50 +#define LOGICAL_CONN_TEXT_LENGTH 450 +#define LOGICAL_CONN_CROSS_SIZE 30 +#define LOGICAL_CONN_MULTI_CIRCLE_RADIUS 15 + +// Broken connection blinking interval (in ms) +#define TIMER_BLINK_INTERVAL 512 + +// Unselected and selected color values for the connections palette against the background color + +#define DARK 112 +#define MID 144 + +#define BACKGROUND 168 + +#define LITE 224 +#define BRITE 255 + +#define LOGICAL_CONN_COLOR_COUNT 7 +#define LOGICAL_CONN_SELECTION_STATES 2 + +static color32 s_pWireColors[LOGICAL_CONN_COLOR_COUNT][LOGICAL_CONN_SELECTION_STATES] = +{ + { { MID, MID, 0, 255 }, /* Mid Yellow */ { BRITE, BRITE, 0, 255 }, /* Bright Yellow */ }, + { { MID, DARK, 0, 255 }, /* Dark Orange */ { BRITE, MID, 0, 255 }, /* Bright Orange */ }, + { { 0, DARK, 0, 255 }, /* Dark Green */ { 0, BRITE, 0, 255 }, /* Bright Green */ }, + { { 0, MID, MID, 255 }, /* Mid Cyan */ { 0, BRITE, BRITE, 255 }, /* Bright Cyan */ }, + { { 0, 0, MID, 255 }, /* Mid Blue */ { 0, MID, BRITE, 255 }, /* Bright Baby Blue */ }, + { { MID, 0, MID, 255 }, /* Mid Magenta */ { BRITE, 0, BRITE, 255 }, /* Bright Magenta */ }, + { { DARK, DARK, DARK, 255 }, /* Dark Gray */ { BRITE, BRITE, BRITE, 255 }, /* Bright White */ }, +}; + +static color32 s_pBrokenWireColor[LOGICAL_CONN_SELECTION_STATES] = +{ + { DARK, 0, 0, 255 }, /* Dark Red */ { BRITE, 0, 0, 255 }, /* Bright Red */ +}; + +//----------------------------------------------------------------------------- +// Purpose: Constructor. Initializes data members. +// --------------------------------------------------------------------------- +CMapViewLogical::CMapViewLogical(void) : m_RenderDict( 0, 1024, DefLessFunc( CMapClass* ) ) +{ + m_bUpdateRenderObjects = true; + SetAxes(AXIS_X, FALSE, AXIS_Y, TRUE); + SetDrawType( VIEW_LOGICAL ); +} + + +//----------------------------------------------------------------------------- +// Purpose: Destructor. Frees dynamically allocated resources. +//----------------------------------------------------------------------------- +CMapViewLogical::~CMapViewLogical(void) +{ +} + + +//----------------------------------------------------------------------------- +// Purpose: First-time initialization of this view. +//----------------------------------------------------------------------------- +void CMapViewLogical::OnInitialUpdate(void) +{ + CreateTitleWindow(); + GetTitleWnd()->SetTitle("Logical"); + SetZoom(0); // Zoom out as far as possible. + UpdateClientView(); + CMapView2DBase::OnInitialUpdate(); + // FIXME: Hardcoded light gray background - should be from a new "Logical View" options settings dialog + m_ClearColor.SetColor( BACKGROUND, BACKGROUND, BACKGROUND, 255 ); + m_clrGrid.SetColor( MID, MID, MID, 255 ); +} + + +//----------------------------------------------------------------------------- +// Purpose: +// Input : point - Point in client coordinates. +// bMakeFirst - +// Output : Returns true on success, false on failure. +//----------------------------------------------------------------------------- +bool CMapViewLogical::SelectAtCascading( const Vector2D &ptClient, bool bMakeFirst ) +{ + CMapDoc *pDoc = GetMapDoc(); + CSelection *pSelection = pDoc->GetSelection(); + + pSelection->ClearHitList(); + + GetHistory()->MarkUndoPosition(pSelection->GetList(), "Selection"); + + // + // Check all the objects in the world for a hit at this point. + // + + HitInfo_t HitData[MAX_PICK_HITS]; + int nHits = ObjectsAt(ptClient, HitData, MAX_PICK_HITS); + + // If there were no hits at the given point, clear selection. + if ( nHits == 0 ) + { + if (bMakeFirst) + { + pDoc->SelectFace(NULL, 0, scClear|scSaveChanges); + pDoc->SelectObject(NULL, scClear|scSaveChanges); + } + + return false; + } + + SelectMode_t eSelectMode = pSelection->GetMode(); + + for ( int i=0; i<nHits; ++i ) + { + CMapClass *pSelObject = HitData[i].pObject->PrepareSelection( eSelectMode ); + if ( !pSelObject ) + continue; + + pSelection->AddHit( pSelObject ); + } + + // + // Select a single object. + // + if ( bMakeFirst ) + { + pDoc->SelectFace( NULL, 0, scClear|scSaveChanges ); + pDoc->SelectObject( NULL, scClear|scSaveChanges ); + } + + pSelection->SetCurrentHit( hitFirst, true ); + + return true; +} + + +//----------------------------------------------------------------------------- +// Base class calls this when render lists need rebuilding +//----------------------------------------------------------------------------- +void CMapViewLogical::OnRenderListDirty() +{ + m_bUpdateRenderObjects = true; +} + + +//----------------------------------------------------------------------------- +// Purpose: Builds up list of mapclasses to render +//----------------------------------------------------------------------------- +void CMapViewLogical::AddToRenderLists( CMapClass *pObject ) +{ +#if _DEBUG && 0 + CMapEntity *pEntity = dynamic_cast<CMapEntity *>(pObject); + if (pEntity) + { + LPCTSTR pszTargetName = pEntity->GetKeyValue("targetname"); + if ( pszTargetName && !strcmp(pszTargetName, "relay_cancelVCDs") ) + { + // Set breakpoint here for debugging this entity's visiblity + int foo = 0; + } + } +#endif + + if ( !pObject->IsVisibleLogical() ) + return; + + // Don't render groups, render their children instead. + if ( !pObject->IsGroup() && pObject->IsLogical() ) + { + Vector2D vecMins, vecMaxs; + pObject->GetRenderLogicalBox( vecMins, vecMaxs ); + +// Always paint all the entities to ensure that any inter-entity connections are visible +// if ( !IsValidBox( vecMins, vecMaxs ) || IsInClientView( vecMins, vecMaxs ) ) + { + // Make sure the object is in the update region. + m_RenderList.AddToTail( pObject ); + m_ConnectionList.AddToTail( pObject ); + m_RenderDict.Insert( pObject ); + } + } + + // Recurse into children and add them. + const CMapObjectList *pChildren = pObject->GetChildren(); + FOR_EACH_OBJ( *pChildren, pos ) + { + AddToRenderLists( pChildren->Element(pos) ); + } +} + + +//----------------------------------------------------------------------------- +// Purpose: Builds up list of mapclasses to render +//----------------------------------------------------------------------------- +void CMapViewLogical::PopulateConnectionList( ) +{ + while ( m_ConnectionUpdate.Count() ) + { + CMapClass *pObject; + m_ConnectionUpdate.Pop( pObject ); + if ( !pObject->IsVisibleLogical() ) + continue; + + // Don't render groups, render their children instead. + if ( !pObject->IsGroup() && pObject->IsLogical() ) + { + // Don't add it if it's visible already + if ( m_RenderDict.Find( pObject ) == m_RenderDict.InvalidIndex() ) + { + CEditGameClass *pClass = dynamic_cast< CEditGameClass * >( pObject ); + if ( pClass ) + { + int nCount = pClass->Connections_GetCount(); + for ( int i = 0; i < nCount; ++i ) + { + CEntityConnection *pConn = pClass->Connections_Get( i ); + + // Find the input entity associated with this connection + CMapEntityList *pEntityList = pConn->GetTargetEntityList(); + + int j; + int nInputCount = pEntityList->Count(); + for ( j = 0; j < nInputCount; ++j ) + { + CMapEntity *pEntity = pEntityList->Element(j); + if ( m_RenderDict.Find( pEntity ) != m_RenderDict.InvalidIndex() ) + { + m_ConnectionList.AddToTail( pObject ); + break; + } + } + if ( j != nInputCount ) + break; + } + } + } + } + + // Recurse into children and add them. + const CMapObjectList *pChildren = pObject->GetChildren(); + FOR_EACH_OBJ( *pChildren, pos ) + { + m_ConnectionUpdate.Push( pChildren->Element(pos) ); + } + } +} + + +//----------------------------------------------------------------------------- +// Purpose: +// Input : nIDEvent - +//----------------------------------------------------------------------------- +void CMapViewLogical::OnTimer(UINT nIDEvent) +{ + if ( nIDEvent == TIMER_CONNECTIONUPDATE ) + { + // Make sure we don't blink too fast + static unsigned int nLastUpdate = 0; + if ( GetTickCount() >= (nLastUpdate+TIMER_BLINK_INTERVAL/2) ) + { + nLastUpdate = GetTickCount(); + UpdateView( 0 ); // Force the view to redraw for blinking errors + } + } + else + CView::OnTimer(nIDEvent); +} + + +const color32 & CMapViewLogical::GetWireColor(const char *pszName, const bool bSelected, const bool bError, const bool bAnySelected) +{ + // Select the connecting color based on the string passed in + // (using OutputName for instance, gives varying "wire colors" by entity output type). + + Assert( LOGICAL_CONN_COLOR_COUNT == (sizeof(s_pWireColors) / sizeof(color32) ) / LOGICAL_CONN_SELECTION_STATES ); + + if ( !bError ) + { + + int nIndex = 0; + + // Has the named passed in + while ( *pszName ) + nIndex += *pszName++; + + // Index based on the number of colors available + nIndex %= LOGICAL_CONN_COLOR_COUNT; + + // Only blink non-errors if the drawing object is selected +// bool bBlinkingState = bSelected ? (GetTickCount() / TIMER_BLINK_INTERVAL) & 1 : 0; + return s_pWireColors[nIndex][bSelected]; + } + else + { + // Only blink errors if nothing is selected, or this is selected + bool bBlinkingState = bSelected || !bAnySelected ? (GetTickCount() / TIMER_BLINK_INTERVAL) & 1 : bSelected; + return s_pBrokenWireColor[bBlinkingState]; + } +} + + +//----------------------------------------------------------------------------- +// Draws a wire from a particular point to a target +//----------------------------------------------------------------------------- +#define BACKWARD_WIRE_OVERSHOOT 50 +#define BACKWARD_WIRE_Y_DISTANCE 150 + +void CMapViewLogical::DrawConnectingWire( float x, float y, CMapEntity *pSource, CEntityConnection *pConnection, CMapEntity *pTarget ) +{ + CRender2D *pRender = GetRender(); + + // FIXME: Deal with bad input type + Vector2D vecEndPosition, vecConnector; + pTarget->GetLogicalConnectionPosition( LOGICAL_CONNECTION_INPUT, vecConnector ); + vecEndPosition = vecConnector; + + int nInputs = pTarget->Upstream_GetCount(); + + // Compensate for multiple inputs -- fan-in the connections from the various pSource entities + BOOL bFound = false; + if ( nInputs ) + { + int nInput; + for ( nInput = 0; nInput < nInputs; nInput++ ) + { + CEntityConnection *pInputConnection = pTarget->Upstream_Get(nInput); + if ( pInputConnection ) + { + if ( pInputConnection == pConnection ) + { + bFound = true; + break; + } + } + } + if (bFound) + { + vecEndPosition.x -= LOGICAL_CONN_SPREAD_DIST; + vecEndPosition.y += ( (nInputs - 1) * LOGICAL_CONN_VERT_SPACING / 2 ) / 2 - (nInput * LOGICAL_CONN_VERT_SPACING / 2); + + pRender->MoveTo( Vector( vecEndPosition.x, vecEndPosition.y, 0.0f) ); + pRender->DrawLineTo( Vector( vecConnector.x, vecConnector.y, 0.0f) ); + } + else + { + Assert(0); + } + } + + pRender->MoveTo( Vector( x, y, 0.0f ) ); + if ( x < vecEndPosition.x ) + { + // Do direct connection + pRender->DrawLineTo( Vector( x, vecEndPosition.y, 0.0f ) ); + pRender->DrawLineTo( Vector( vecEndPosition.x, vecEndPosition.y, 0.0f ) ); + return; + } + + Vector2D vecTargetMins, vecTargetMaxs; + pTarget->GetRenderLogicalBox( vecTargetMins, vecTargetMaxs ); + vecTargetMins.y -= BACKWARD_WIRE_Y_DISTANCE; + vecTargetMaxs.y += BACKWARD_WIRE_Y_DISTANCE; + + float flHalfY = ( y + vecEndPosition.y ) * 0.5f; + if ( flHalfY > vecTargetMins.y && flHalfY < vecTargetMaxs.y ) + { + flHalfY = ( flHalfY < vecEndPosition.y ) ? vecTargetMins.y : vecTargetMaxs.y; + } + + pRender->DrawLineTo( Vector( x, flHalfY, 0.0f ) ); + pRender->DrawLineTo( Vector( vecEndPosition.x - BACKWARD_WIRE_OVERSHOOT, flHalfY, 0.0f ) ); + pRender->DrawLineTo( Vector( vecEndPosition.x - BACKWARD_WIRE_OVERSHOOT, vecEndPosition.y, 0.0f ) ); + pRender->DrawLineTo( Vector( vecEndPosition.x, vecEndPosition.y, 0.0f ) ); +} + +void CMapViewLogical::RenderConnections(const bool bDrawSelected, const bool bAnySelected) +{ + Vector2D pt, pt2; + WorldToClient( pt, Vector( 0.0f, 0.0f, 0.0f ) ); + WorldToClient( pt2, Vector( 0.0f, LOGICAL_CONN_VERT_SPACING, 0.0f ) ); + + int nCount = m_ConnectionList.Count(); + + for ( int i = 0; i < nCount ; ++i ) + { + CMapEntity *pMapClass = dynamic_cast<CMapEntity*>( m_ConnectionList[i] ); + if ( !pMapClass ) + continue; + + CEditGameClass *pEditClass = dynamic_cast<CEditGameClass*>( pMapClass ); + if ( !pEditClass ) + continue; + + int nConnectionCount = pEditClass->Connections_GetCount(); + if ( nConnectionCount == 0 ) + continue; + + Vector2D vecStartPosition; + pMapClass->GetLogicalConnectionPosition( LOGICAL_CONNECTION_OUTPUT, vecStartPosition ); + + CRender2D *pRender = GetRender(); + + float x = vecStartPosition.x + LOGICAL_CONN_SPREAD_DIST + LOGICAL_CONN_TEXT_LENGTH; + float y = vecStartPosition.y + ( nConnectionCount - 1 ) * LOGICAL_CONN_VERT_SPACING / 2; + + for ( int j = 0; j < nConnectionCount; ++j ) + { + CEntityConnection *pConn = pEditClass->Connections_Get( j ); + + // Find the input entity associated with this connection + CMapEntityList *pEntityList = pConn->GetTargetEntityList(); + int nInputCount = pEntityList->Count(); + bool bBadInput = !MapEntityList_HasInput( pEntityList, pConn->GetInputName() ); + bool bBadConnection = (nInputCount == 0); + + // Stop drawing entity connection text once the entity itself gets too small. + bool bDrawOutput = ( fabs( pt.y - pt2.y ) >= 16 ); + bool bDrawDelay = ( fabs( pt.y - pt2.y ) >= 20 ); + bool bDrawInput = ( fabs( pt.y - pt2.y ) >= 24 ); + bool bDrawTarget = ( fabs( pt.y - pt2.y ) >= 28 ); + + bool bEntitySelected = ( pMapClass->GetSelectionState() != SELECT_NONE ); + bool bInputSelected = false; + + for ( int k = 0; k < nInputCount; ++k ) + { + if ( pEntityList->Element( k )->GetSelectionState() != SELECT_NONE ) + bInputSelected = true; + + // Make sure all the connected entities are all visible + if ( pEntityList->Element( k )->IsVisibleLogical() == false ) + bBadConnection = true; + } + + // Make sure we only draw the selected OR unselected connections as requested + if ( bDrawSelected == (bEntitySelected || bInputSelected) ) + { + color32 c = GetWireColor( pConn->GetOutputName(), + bEntitySelected || bInputSelected, + bBadConnection || bBadInput, + bAnySelected ); + + if ( bDrawDelay || bDrawOutput || bDrawInput || bDrawTarget ) + { + char pBuf[1024]; + + pRender->SetTextColor( c.r, c.g, c.b ); + + int nChars = 0; + if ( bDrawOutput ) + nChars += Q_snprintf( pBuf+nChars, sizeof(pBuf), "%s", pConn->GetOutputName() ); + if ( bDrawDelay ) + nChars += Q_snprintf( pBuf+nChars, sizeof(pBuf), "(%.2f)", pConn->GetDelay() ); + + if ( nChars ) + pRender->DrawText( pBuf, Vector2D( vecStartPosition.x + LOGICAL_CONN_SPREAD_DIST, y ), 2, 1, CRender2D::TEXT_JUSTIFY_TOP | CRender2D::TEXT_JUSTIFY_RIGHT ); + + nChars = 0; + if ( bDrawInput ) + nChars += Q_snprintf( pBuf+nChars, sizeof(pBuf), "%s", pConn->GetInputName() ); + if ( bDrawTarget ) + nChars += Q_snprintf( pBuf+nChars, sizeof(pBuf), "[%s] ", pConn->GetTargetName() ); + + if ( nChars ) + pRender->DrawText( pBuf, Vector2D( vecStartPosition.x + LOGICAL_CONN_SPREAD_DIST, y ), 2, -1, CRender2D::TEXT_JUSTIFY_BOTTOM | CRender2D::TEXT_JUSTIFY_RIGHT ); + } + + pRender->SetDrawColor( c.r, c.g, c.b ); + pRender->MoveTo( Vector( vecStartPosition.x, vecStartPosition.y, 0.0f ) ); + pRender->DrawLineTo( Vector( vecStartPosition.x + LOGICAL_CONN_SPREAD_DIST, y, 0.0f ) ); + pRender->DrawLineTo( Vector( x, y, 0.0f ) ); + + if ( bBadConnection ) + { + // Draw an X for a bogus connection. + pRender->MoveTo( Vector( x - LOGICAL_CONN_CROSS_SIZE, y - LOGICAL_CONN_CROSS_SIZE, 0.0f ) ); + pRender->DrawLineTo( Vector( x + LOGICAL_CONN_CROSS_SIZE, y + LOGICAL_CONN_CROSS_SIZE, 0.0f ) ); + pRender->MoveTo( Vector( x - LOGICAL_CONN_CROSS_SIZE, y + LOGICAL_CONN_CROSS_SIZE, 0.0f ) ); + pRender->DrawLineTo( Vector( x + LOGICAL_CONN_CROSS_SIZE, y - LOGICAL_CONN_CROSS_SIZE, 0.0f ) ); + } + else if ( nInputCount == 1 ) + { + DrawConnectingWire( x, y, pMapClass, pConn, pEntityList->Element( 0 ) ); + } + else + { + // Draw a circle for multiple connections + pRender->DrawCircle( Vector( x + LOGICAL_CONN_MULTI_CIRCLE_RADIUS, y, 0.0f ), LOGICAL_CONN_MULTI_CIRCLE_RADIUS ); + + float mx = x + LOGICAL_CONN_SPREAD_DIST; + float my = y + ( nInputCount / 2 ) * LOGICAL_CONN_VERT_SPACING/2; + + Vector vecStart( x + LOGICAL_CONN_MULTI_CIRCLE_RADIUS, y, 0.0f ); + for ( int k = 0; k < nInputCount; ++k ) + { + // bBadInput = false; // This should be based on whether downstream entity has the specificied named input + bInputSelected = ( pEntityList->Element( k )->GetSelectionState() != SELECT_NONE ); + color32 col = GetWireColor( pConn->GetOutputName(), + bEntitySelected || bInputSelected, + bBadInput, + bAnySelected ); + + pRender->SetDrawColor( col.r, col.g, col.b ); + + Vector vecEnd( mx, my, 0.0f ); + Vector vecDelta; + VectorSubtract( vecEnd, vecStart, vecDelta ); + VectorNormalize( vecDelta ); + vecDelta *= LOGICAL_CONN_MULTI_CIRCLE_RADIUS; + vecDelta += vecStart; + + pRender->MoveTo( vecDelta ); + pRender->DrawLineTo( Vector( mx, my, 0.0f ) ); + DrawConnectingWire( mx, my, pMapClass, pConn, pEntityList->Element( k ) ); + + mx += LOGICAL_CONN_HORIZ_SPACING; + my -= LOGICAL_CONN_VERT_SPACING/2; + } + } + } + + x += LOGICAL_CONN_HORIZ_SPACING * (nInputCount+1) + 2*LOGICAL_CONN_MULTI_CIRCLE_RADIUS; + y -= LOGICAL_CONN_VERT_SPACING; + } + } +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CMapViewLogical::Render() +{ + CMapDoc *pDoc = GetMapDoc(); + CMapWorld *pWorld = pDoc->GetMapWorld(); + + GetRender()->StartRenderFrame(); + + // Draw grid if enabled. + if ( pDoc->m_bShowLogicalGrid ) + { + DrawGridLogical( GetRender() ); + } + + // Draw the world if we have one. + if (pWorld == NULL) + return; + + // Traverse the entire world, sorting visible elements into two arrays: + // Normal objects and selected objects, so that we can render the selected + // objects last. + if ( m_bUpdateRenderObjects ) + { + m_bUpdateRenderObjects = false; + + m_RenderList.RemoveAll(); + m_RenderDict.RemoveAll(); + m_ConnectionList.RemoveAll(); + m_ConnectionUpdate.Clear(); + + // fill render lists with visible objects + AddToRenderLists( pWorld ); + + // Add the connections too + m_ConnectionUpdate.Push( pWorld ); + PopulateConnectionList(); + + // Make sure we have a timer running to drive error animations + SetTimer( TIMER_CONNECTIONUPDATE, TIMER_BLINK_INTERVAL, NULL); + } + + // Assume we are blinking, unless something is selected. + bool bAnySelected = FALSE; + + CUtlVector<CMapClass *> selectedObjects; + CUtlVector<CMapClass *> helperObjects; + CUtlVector<CMapClass *> unselectedObjects; + + // Render normal (nonselected) objects first + for (int i = 0; i < m_RenderList.Count(); i++) + { + CMapClass *pObject = m_RenderList[i]; + + if ( pObject->IsSelected() ) + { + bAnySelected = TRUE; + + // render later + if ( pObject->GetToolObject( 0, false ) ) + { + helperObjects.AddToTail( pObject ); + } + else + { + selectedObjects.AddToTail( pObject ); + } + } + else + { + unselectedObjects.AddToTail( pObject ); + } + } + + // Render unselected connections first + RenderConnections(false, bAnySelected); + + // render unselected objects next, on top of the connections + for ( int j = 0; j < unselectedObjects.Count(); j++ ) + { + unselectedObjects[j]->RenderLogical( GetRender() ); + } + + if ( bAnySelected ) + { + // Render selected objects in second batch, so they overdraw normal object + for (int i = 0; i < selectedObjects.Count(); i++) + { + selectedObjects[i]->RenderLogical( GetRender() ); + } + + // Render selected connections on top of everything else + RenderConnections(true, bAnySelected); + } + + // render all tools + CBaseTool *pCurTool = pDoc->GetTools()->GetActiveTool(); + + int nToolCount = pDoc->GetTools()->GetToolCount(); + for (int i = 0; i < nToolCount; i++) + { + CBaseTool *pTool = pDoc->GetTools()->GetTool(i); + if ((pTool != NULL) && (pTool != pCurTool)) + { + pTool->RenderToolLogical( GetRender() ); + } + } + + // render active tool over all other tools + if ( pCurTool ) + { + pCurTool->RenderToolLogical( GetRender() ); + } + + // render map helpers at last + for (int i = 0; i < helperObjects.Count(); i++) + { + helperObjects[i]->RenderLogical( GetRender() ); + } + + GetRender()->EndRenderFrame(); +} + + +//----------------------------------------------------------------------------- +// convert client view space to map world coordinates (2D versions for convenience) +//----------------------------------------------------------------------------- +void CMapViewLogical::WorldToClient( Vector2D &ptClient, const Vector2D &vWorld ) +{ + Vector vWorld3D( vWorld.x, vWorld.y, 0.0f ); + CMapView2DBase::WorldToClient( ptClient, vWorld3D ); +} + +void CMapViewLogical::ClientToWorld( Vector2D &vWorld, const Vector2D &vClient ) +{ + Vector vWorld3D; + CMapView2DBase::ClientToWorld( vWorld3D, vClient ); + vWorld.x = vWorld3D.x; + vWorld.y = vWorld3D.y; +} + +void CMapViewLogical::WorldToClient( Vector2D &ptClient, const Vector &vWorld ) +{ + CMapView2DBase::WorldToClient( ptClient, vWorld ); +} + +void CMapViewLogical::ClientToWorld( Vector &vWorld, const Vector2D &vClient ) +{ + CMapView2DBase::ClientToWorld( vWorld, vClient ); +} |