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.
710 lines
22 KiB
710 lines
22 KiB
//========= 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 ); |
|
}
|
|
|