Files
GTASource/game/vehicleAi/JunctionEditor.cpp
expvintl 419f2e4752 init
2025-02-23 17:40:52 +08:00

1761 lines
56 KiB
C++

#include "vehicleAi/JunctionEditor.h"
#if __JUNCTION_EDITOR
#include "bank\bkmgr.h"
#include "bank\slider.h"
#include "camera\CamInterface.h"
#include "camera\debug\DebugDirector.h"
#include "camera\helpers\Frame.h"
#include "debug\DebugScene.h"
#include "fwdebug\debugdraw.h"
#include "peds\PlayerInfo.h"
#include "peds/Ped.h"
#include "text\text.h"
#include "text\textformat.h"
#include "text\textconversion.h"
#include "vehicleAi\pathfind.h"
#include "fwvehicleai\pathfindtypes.h"
#include "fwmaths\angle.h"
AI_OPTIMISATIONS()
AI_VEHICLE_OPTIMISATIONS()
s32 CJunctionEditor::m_iMode = CJunctionEditor::Mode_Normal;
bool CJunctionEditor::m_bRebindEntrances = false;
bool CJunctionEditor::m_bMustReinitialiseWidgets = false;
bool CJunctionEditor::m_bInitialised = false;
bool CJunctionEditor::m_bActivated = false;
bool CJunctionEditor::m_bJustActivated = false;
bool CJunctionEditor::m_bShowEditor = true;
bool CJunctionEditor::m_bToggleActivation = false;
bkWidget * CJunctionEditor::m_pActivateButton = NULL;
bkWidget * CJunctionEditor::m_pMainGroup = NULL;
bkWidget * CJunctionEditor::m_pCurrentJunctionGroup = NULL;
bkSlider * CJunctionEditor::m_pCurrentJunctionSlider = NULL;
bkSlider * CJunctionEditor::m_pCurrentEntranceSlider = NULL;
bkSlider * CJunctionEditor::m_pNumPhasesSlider = NULL;
bkGroup * CJunctionEditor::m_pEntrancesGroup = NULL;
bkGroup * CJunctionEditor::m_pPhasesGroup = NULL;
bkGroup * CJunctionEditor::m_pTrafficLightLocationsGroup = NULL;
bkSlider * CJunctionEditor::m_pTrafficLightSearchDistanceSlider = NULL;
bkSlider * CJunctionEditor::m_pTrafficLightIndexSlider = NULL;
bkText * CJunctionEditor::m_pTrafficLightText = NULL;
bkText * CJunctionEditor::m_pNumTrafficLightsText = NULL;
bkButton * CJunctionEditor::m_pTrafficLightPickButton = NULL;
bkButton * CJunctionEditor::m_pTrafficLightClearButton = NULL;
CJunctionEditor::EntranceBankVars CJunctionEditor::m_EntranceBankVars[MAX_ROADS_INTO_JUNCTION];
CJunctionEditor::PhaseVars CJunctionEditor::m_PhaseVars[MAX_ROADS_INTO_JUNCTION];
bool CJunctionEditor::m_bSelectAnyNodeAsJunction = false;
s32 CJunctionEditor::m_iNumJunctions = 0;
s32 CJunctionEditor::m_iCurrentJunction = -1;
s32 CJunctionEditor::m_iSecondaryJunction = -1;
s32 CJunctionEditor::m_iCurrentTrafficLight = 0;
char CJunctionEditor::m_CurrentTrafficLightText[256] = { 0 };
char CJunctionEditor::m_NumTrafficLightsText[64] = { 0 };
s32 CJunctionEditor::m_iDesiredJunction = -1;
bool CJunctionEditor::m_iCurrentJunctionIsRailwayCrossing = false;
bool CJunctionEditor::m_iCurrentJunctionCanSkipPedPhase = false;
float CJunctionEditor::m_fCurrentJunctionPhaseOffset = 0.0f;
CAutoJunctionAdjustment CJunctionEditor::m_CurrentAutoJuncAdj;
//s32 CJunctionEditor::m_iCurrentEntrance = -1;
//s32 CJunctionEditor::m_iDesiredEntrance = -1;
s32 CJunctionEditor::m_iNumPhases = 0;
void GetJunctionsNodesLinkedToJunctionNode(const CPathNode * pNode, const CPathNode ** ppLinkedNodes, const s32 iMaxLinkedNodes, s32 & iNumLinkedNodes);
void GetEntrancesLinkedToJunctions(const CPathNode ** ppJunctionNodes, const s32 iNumJunctionNodes, const CPathNode ** ppEntranceNodes, const s32 iMaxNumEntranceNodes, s32 & iNumEntranceNodes);
const float fMaxDistSqr = 100.0f * 100.0f;
const Vector3 vRaiseJunction(0.0f,0.0f,5.0f);
const Vector3 vRaiseLink(0.0f,0.0f,3.0f);
const Vector3 vRaiseJoin(0.0f,0.0f,1.0f);
const Color32 iJunctionCol = Color_green2;
const Color32 iEntranceCol = Color_yellow2;
const Color32 iEntranceColSlipLane = Color_DarkGoldenrod;
const Color32 iLinkCol = Color_green4;
const Vector3 g_vInvalidJunctionNodePosition(64000.0f, 64000.0f, 64000.0f);
CJunctionEditor::CJunctionEditor()
{
}
CJunctionEditor::~CJunctionEditor()
{
}
void CJunctionEditor::Init()
{
memset(m_EntranceBankVars, 0, sizeof(EntranceBankVars)*MAX_ROADS_INTO_JUNCTION);
memset(m_PhaseVars, 0, sizeof(PhaseVars)*MAX_ROADS_INTO_JUNCTION);
m_iNumJunctions = CJunctions::GetNumJunctionTemplates();
InitWidgets(true);
}
void CJunctionEditor::ToggleEditor()
{
m_bActivated = !m_bActivated;
m_bToggleActivation = true;
}
bkBank * CJunctionEditor::GetBank()
{
const char * pBankName = "Vehicle AI and Nodes";
bkBank * pBank = BANKMGR.FindBank(pBankName);
Assertf(pBank, "Ok, who moved the \"%s\" bank?", pBankName);
return pBank;
}
void CJunctionEditor::InitWidgets(bool bFirstTime)
{
bkBank * pBank = GetBank();
if(!pBank)
{
return;
}
if(bFirstTime)
{
m_pMainGroup = pBank->PushGroup("Junction Editor");
m_pActivateButton = (bkWidget*)pBank->AddButton("Activate Junction Editor", ToggleEditor);
pBank->PopGroup();
}
if(m_bActivated)
{
SetInitialNodesDebug();
pBank->Remove(*m_pActivateButton);
pBank->Remove(*m_pMainGroup);
m_pMainGroup = pBank->PushGroup("Junction Editor");
pBank->AddToggle("Show Editor", &m_bShowEditor);
pBank->AddToggle("Debug Junctions", &CJunctions::m_bDebug);
pBank->AddToggle("Debug Wait For Traffic", &CJunctions::m_bDebugWaitForTraffic);
pBank->AddToggle("Debug Junction Text", &CJunctions::m_bDebugText);
// >> Save / Load
m_pCurrentJunctionGroup = pBank->PushGroup("Save / Load");
pBank->AddButton("Save All Templates", CJunctions::EditorSaveJunctionTemplates);
pBank->AddButton("Load All Templates", CJunctions::EditorLoadJunctionTemplates);
safecpy(CJunctions::ms_JunctionEditorXmlFilename, "common:/data/levels/gta5/junctions.xml");
pBank->AddText("XML Filename", CJunctions::ms_JunctionEditorXmlFilename, RAGE_MAX_PATH);
pBank->AddButton("Save All Templates (XML)", CJunctions::EditorSaveJunctionTemplatesXml);
pBank->AddButton("Load All Templates (XML)", CJunctions::EditorLoadJunctionTemplatesXml);
pBank->PopGroup(); // < Save / Load
pBank->AddButton("Refresh all junctions", CJunctions::RefreshAllJunctions);
// > Junction Editor
{
pBank->AddText("Num Junctions", &m_iNumJunctions);
m_pCurrentJunctionSlider = pBank->AddSlider("Current Junction", &m_iDesiredJunction, -1, m_iNumJunctions-1, 1);
pBank->AddSlider("Secondary Junction", &m_iSecondaryJunction, -1, m_iNumJunctions-1, 1);
pBank->AddButton("Warp here!", WarpToCurrentJunctionTemplate);
pBank->AddButton("Add New Junction", NewJunction);
pBank->AddButton("Delete Current Junction", DeleteJunction);
pBank->AddToggle("Allow any node as junction", &m_bSelectAnyNodeAsJunction);
m_pCurrentJunctionGroup = pBank->PushGroup("Current Junction");
// > Current Junction
{
pBank->AddButton("Select Junction Node", StartSelectJunctionNode);
pBank->AddButton("Reposition junction and entrances", SelectAndRebindJunctionAndEntrances);
pBank->AddButton("Add new phase", AddNewPhase);
pBank->AddToggle("Railway Crossing", &m_iCurrentJunctionIsRailwayCrossing, OnToggleIsRailwayCrossing);
pBank->AddToggle("Disable Skip Ped Phase", &m_iCurrentJunctionCanSkipPedPhase, OnToggleDisableSkipPedPhase);
pBank->AddSlider("Phase Offset", &m_fCurrentJunctionPhaseOffset, 0.0f, 100.0f, 0.1f, OnChangePhaseOffset);
m_pNumPhasesSlider = pBank->AddSlider("Num Phases", &m_iNumPhases, 0, 0, 1, OnSetNumPhases);
m_pEntrancesGroup = pBank->PushGroup("Entrances");
// > Entrances
{
pBank->PopGroup(); // < Entrances
}
m_pPhasesGroup = pBank->PushGroup("Phases");
// > Phases
{
pBank->PopGroup(); // < Phases
}
m_pTrafficLightLocationsGroup = pBank->PushGroup("Traffic Lights");
m_pNumTrafficLightsText = pBank->AddText("Num Lights Defined : ", m_NumTrafficLightsText, 256 );
m_pTrafficLightIndexSlider = pBank->AddSlider("Lights", &m_iCurrentTrafficLight, 0, MAX_TRAFFIC_LIGHT_LOCATIONS-1, 1, OnChangeCurrentTrafficLight);
m_pTrafficLightText = pBank->AddText("Position:", m_CurrentTrafficLightText, 256 );
m_pTrafficLightPickButton = pBank->AddButton("Pick Location", OnPickTrafficLightLocation);
m_pTrafficLightClearButton = pBank->AddButton("Clear", OnClearTrafficLight);
// > Traffic Light Locations
{
pBank->PopGroup(); // < Traffic Light Locations
}
pBank->PopGroup(); // < Current Junction
}
pBank->PushGroup("AutoJunction Adjustments");
{
pBank->AddButton("Find or create adjustment for nearest junction", FindOrCreateAutoAdjustment);
pBank->AddButton("Remove Adjustment from nearest junction", RemoveAutoAdjustment);
pBank->AddVector("Junction Pos", &m_CurrentAutoJuncAdj.m_vLocation, -10000.0f, 10000.0f, 0.0f);
pBank->AddSlider("Cycle Offset", &m_CurrentAutoJuncAdj.m_fCycleOffset, 0.0f, 1000.0f, 0.1f, OnUpdateAutoAdjustment);
pBank->AddSlider("Cycle Duration", &m_CurrentAutoJuncAdj.m_fCycleDuration, 0.0f, 1000.0f, 0.1f, OnUpdateAutoAdjustment);
pBank->PopGroup(); // < AutoJunction Adjustments
}
pBank->PopGroup(); // < Junction Editor
}
m_bJustActivated = true;
}
}
void CJunctionEditor::Update()
{
if(!m_bShowEditor)
return;
if(!m_bInitialised)
{
Init();
m_bInitialised = true;
}
if(m_bMustReinitialiseWidgets)
{
InitJunctionWidgets();
m_bMustReinitialiseWidgets = false;
}
if(m_bToggleActivation)
{
m_bToggleActivation = false;
InitWidgets(false);
return;
}
if(!m_bActivated)
return;
if(m_bJustActivated)
{
// Make sure 'm_pMainGroup' is visible & selected as current
//bkBank * pBank = GetBank();
//if(pBank)
// pBank->SetCurrentGroup( *((bkGroup*)m_pMainGroup) );
m_bJustActivated = false;
}
if(m_iDesiredJunction != -1 && m_iDesiredJunction != m_iCurrentJunction)
{
m_iCurrentJunction = m_iDesiredJunction;
OnUseJunctionSlider();
}
switch(m_iMode)
{
case Mode_SelectJunctionNode:
{
UpdateSelectNode();
break;
}
case Mode_SelectTrafficLightLocation:
{
UpdateSelectTrafficLightLocation();
break;
}
/*
case Mode_SelectEntranceNode:
{
UpdateSelectNode();
break;
}
*/
default:
break;
}
CJunctions::m_bDebugLights = true;
// Ensure that all pathnodes's junction nodes are marked as "m_qualifiesAsJunction"
for(int j=0; j<CJunctions::GetNumJunctionTemplates(); j++)
{
CJunctionTemplate & temp = CJunctions::GetJunctionTemplate(j);
if(temp.m_iFlags & CJunctionTemplate::Flag_NonEmpty)
{
//----------------------------------------------------------------------------------------------------
// For all junction nodes in templated junctions, ensure that the "m_qualifiesAsJunction" flag is set
for(int n=0; n<temp.m_iNumJunctionNodes; n++)
{
const Vector3 & vNodePos = temp.m_vJunctionNodePositions[n];
CNodeAddress node = ThePaths.FindNodeClosestToCoors(vNodePos, 4.0f);
if(!node.IsEmpty())
{
CPathNode * pJunctionNode = ThePaths.FindNodePointerSafe(node);
if(pJunctionNode)
{
pJunctionNode->m_2.m_qualifiesAsJunction = true;
}
}
}
}
}
}
void CJunctionEditor::OnUseJunctionSlider()
{
m_bMustReinitialiseWidgets = true;
//InitJunctionWidgets();
}
void CJunctionEditor::OnToggleDisableSkipPedPhase()
{
if(m_iCurrentJunction != -1)
{
CJunctionTemplate & temp = CJunctions::GetJunctionTemplate(m_iCurrentJunction);
if(m_iCurrentJunctionCanSkipPedPhase)
temp.m_iFlags &= ~CJunctionTemplate::Flag_DisableSkipPedLightPhase;
else
temp.m_iFlags |= CJunctionTemplate::Flag_DisableSkipPedLightPhase;
}
}
void CJunctionEditor::OnToggleIsRailwayCrossing()
{
if(m_iCurrentJunction != -1)
{
CJunctionTemplate & temp = CJunctions::GetJunctionTemplate(m_iCurrentJunction);
if(m_iCurrentJunctionIsRailwayCrossing)
temp.m_iFlags |= CJunctionTemplate::Flag_RailwayCrossing;
else
temp.m_iFlags &= ~CJunctionTemplate::Flag_RailwayCrossing;
}
}
void CJunctionEditor::OnChangePhaseOffset()
{
if (m_iCurrentJunction != -1)
{
CJunctionTemplate& temp = CJunctions::GetJunctionTemplate(m_iCurrentJunction);
temp.m_fPhaseOffset = m_fCurrentJunctionPhaseOffset;
}
}
void CJunctionEditor::OnChangeCurrentTrafficLight()
{
Assert(m_iCurrentTrafficLight >= 0 && m_iCurrentTrafficLight < MAX_TRAFFIC_LIGHT_LOCATIONS);
m_iCurrentTrafficLight = Clamp(m_iCurrentTrafficLight, 0, MAX_TRAFFIC_LIGHT_LOCATIONS-1);
CJunctionTemplate & temp = CJunctions::GetJunctionTemplate(m_iCurrentJunction);
atRangeArray<CJunctionTemplate::CTrafficLightLocation,MAX_TRAFFIC_LIGHT_LOCATIONS> & lights = temp.m_TrafficLightLocations;
// Never allow the user to advance the index beyond the last invalid entry
// This ensures that we always have a packed zero-based array with valid values
if(m_iCurrentTrafficLight > 0)
{
while(m_iCurrentTrafficLight > 0 && lights[m_iCurrentTrafficLight-1].m_iPosX == TRAFFIC_LIGHT_POS_INVALID)
m_iCurrentTrafficLight--;
}
SetCurrentTrafficLightText();
}
void CJunctionEditor::OnPickTrafficLightLocation()
{
if(m_iCurrentJunction != -1)
{
m_iMode = Mode_SelectTrafficLightLocation;
}
}
void CJunctionEditor::OnClearTrafficLight()
{
CJunctionTemplate & temp = CJunctions::GetJunctionTemplate(m_iCurrentJunction);
atRangeArray<CJunctionTemplate::CTrafficLightLocation,MAX_TRAFFIC_LIGHT_LOCATIONS> & lights = temp.m_TrafficLightLocations;
if(lights[m_iCurrentTrafficLight].m_iPosX == TRAFFIC_LIGHT_POS_INVALID)
return;
int i;
for(i=m_iCurrentTrafficLight; i<MAX_TRAFFIC_LIGHT_LOCATIONS-1; i++)
{
lights[i] = lights[i+1];
}
lights[MAX_TRAFFIC_LIGHT_LOCATIONS-1].m_iPosX = TRAFFIC_LIGHT_POS_INVALID;
lights[MAX_TRAFFIC_LIGHT_LOCATIONS-1].m_iPosY = TRAFFIC_LIGHT_POS_INVALID;
lights[MAX_TRAFFIC_LIGHT_LOCATIONS-1].m_iPosZ = TRAFFIC_LIGHT_POS_INVALID;
temp.m_iNumTrafficLightLocations--;
Assert(temp.m_iNumTrafficLightLocations >= 0);
temp.m_iNumTrafficLightLocations = Max(temp.m_iNumTrafficLightLocations, 0);
SetCurrentTrafficLightText();
}
void CJunctionEditor::AddNewPhase()
{
if(m_iCurrentJunction != -1 && m_iCurrentJunction < m_iNumJunctions)
{
if(m_iNumPhases < MAX_ROADS_INTO_JUNCTION)
{
m_iNumPhases++;
CJunctionTemplate & temp = CJunctions::GetJunctionTemplate(m_iCurrentJunction);
temp.m_iNumPhases = m_iNumPhases;
//InitJunctionWidgets();
m_bMustReinitialiseWidgets = true;
}
}
}
void CJunctionEditor::OnSetNumPhases()
{
if(m_iCurrentJunction != -1 && m_iCurrentJunction < m_iNumJunctions)
{
if(m_iNumPhases == 0)
{
m_iNumPhases = 1;
}
CJunctionTemplate & temp = CJunctions::GetJunctionTemplate(m_iCurrentJunction);
temp.m_iNumPhases = m_iNumPhases;
m_bMustReinitialiseWidgets = true;
}
}
void CJunctionEditor::WarpToCurrentJunctionTemplate()
{
if(m_iCurrentJunction != -1)
{
CJunctionTemplate & temp = CJunctions::GetJunctionTemplate(m_iCurrentJunction);
if(temp.m_iNumJunctionNodes && (temp.m_iFlags & CJunctionTemplate::Flag_NonEmpty)!=0)
{
CPed * pPlayer = FindPlayerPed();
if(pPlayer)
{
Vector3 vHeightAboveJunction(0.0f, 0.0f, 50.0f);
Vector3 vFront(0.0f, 0.0f, -1.0f);
Vector3 vUp(0.0f, 1.0f, 0.0f);
camDebugDirector & debugDirector = camInterface::GetDebugDirector();
debugDirector.ActivateFreeCam();
debugDirector.GetFreeCamFrameNonConst().SetPosition(temp.m_vJunctionNodePositions[0] + vHeightAboveJunction);
debugDirector.GetFreeCamFrameNonConst().SetWorldMatrixFromFrontAndUp( vFront, vUp );
pPlayer->Teleport(temp.m_vJunctionNodePositions[0], pPlayer->GetCurrentHeading());
}
}
}
}
void CJunctionEditor::NextJunction()
{
if(m_iNumJunctions == 0)
{
m_iCurrentJunction = -1;
}
else
{
m_iCurrentJunction++;
if(m_iCurrentJunction >= m_iNumJunctions)
m_iCurrentJunction = 0;
}
//InitJunctionWidgets();
m_bMustReinitialiseWidgets = true;
}
void CJunctionEditor::PrevJunction()
{
if(m_iNumJunctions == 0)
{
m_iCurrentJunction = -1;
}
else
{
m_iCurrentJunction--;
if(m_iCurrentJunction < 0)
m_iCurrentJunction = m_iNumJunctions-1;
}
//InitJunctionWidgets();
m_bMustReinitialiseWidgets = true;
}
void CJunctionEditor::InitJunctionWidgets()
{
const char * pBankName = "Vehicle AI and Nodes";
bkBank * pBank = BANKMGR.FindBank(pBankName);
Assertf(pBank, "Ok, who moved the \"%s\" bank?", pBankName);
if(!pBank)
return;
//----------------------------------------------
// Wipe out all the existing 'entrances' groups
pBank->SetCurrentGroup(*m_pEntrancesGroup);
s32 e;
for(e=0; e<MAX_ROADS_INTO_JUNCTION; e++)
{
if(m_EntranceBankVars[e].m_pPhaseSlider)
pBank->Remove(*m_EntranceBankVars[e].m_pPhaseSlider);
if(m_EntranceBankVars[e].m_pStoppingLineSlider)
pBank->Remove(*m_EntranceBankVars[e].m_pStoppingLineSlider);
if(m_EntranceBankVars[e].m_pOrientationSlider)
pBank->Remove(*m_EntranceBankVars[e].m_pOrientationSlider);
if(m_EntranceBankVars[e].m_pLeftFilterPhaseSlider)
pBank->Remove(*m_EntranceBankVars[e].m_pLeftFilterPhaseSlider);
if(m_EntranceBankVars[e].m_pLeftLaneAheadOnlyToggle)
pBank->Remove(*m_EntranceBankVars[e].m_pLeftLaneAheadOnlyToggle);
if(m_EntranceBankVars[e].m_pCanTurnRightToggle)
pBank->Remove(*m_EntranceBankVars[e].m_pCanTurnRightToggle);
if (m_EntranceBankVars[e].m_pRightLaneIsRightOnlyToggle)
pBank->Remove(*m_EntranceBankVars[e].m_pRightLaneIsRightOnlyToggle);
if(m_EntranceBankVars[e].m_pGroup)
pBank->DeleteGroup(*m_EntranceBankVars[e].m_pGroup);
m_EntranceBankVars[e].m_pPhaseSlider = NULL;
m_EntranceBankVars[e].m_pStoppingLineSlider = NULL;
m_EntranceBankVars[e].m_pOrientationSlider = NULL;
m_EntranceBankVars[e].m_pLeftFilterPhaseSlider = NULL;
m_EntranceBankVars[e].m_pLeftLaneAheadOnlyToggle = NULL;
m_EntranceBankVars[e].m_pCanTurnRightToggle = NULL;
m_EntranceBankVars[e].m_pRightLaneIsRightOnlyToggle = NULL;
m_EntranceBankVars[e].m_pGroup = NULL;
}
pBank->UnSetCurrentGroup(*m_pEntrancesGroup);
//-------------------------------------
// Likewise for all the phase groups
pBank->SetCurrentGroup(*m_pPhasesGroup);
s32 p;
for(p=0; p<MAX_ROADS_INTO_JUNCTION; p++)
{
if(m_PhaseVars[p].m_pTime)
pBank->Remove(*m_PhaseVars[p].m_pTime);
if(m_PhaseVars[p].m_pGroup)
pBank->DeleteGroup(*m_PhaseVars[p].m_pGroup);
m_PhaseVars[p].m_pTime = NULL;
m_PhaseVars[p].m_pGroup = NULL;
}
pBank->UnSetCurrentGroup(*m_pPhasesGroup);
//-----------------------------------
// Delete all the traffic-light info
pBank->SetCurrentGroup(*m_pTrafficLightLocationsGroup);
if(m_pNumTrafficLightsText)
pBank->Remove(*m_pNumTrafficLightsText);
if(m_pTrafficLightSearchDistanceSlider)
pBank->Remove(*m_pTrafficLightSearchDistanceSlider);
if(m_pTrafficLightIndexSlider)
pBank->Remove(*m_pTrafficLightIndexSlider);
if(m_pTrafficLightText)
pBank->Remove(*m_pTrafficLightText);
if(m_pTrafficLightPickButton)
pBank->Remove(*m_pTrafficLightPickButton);
if(m_pTrafficLightClearButton)
pBank->Remove(*m_pTrafficLightClearButton);
m_pNumTrafficLightsText = NULL;
m_pTrafficLightSearchDistanceSlider = NULL;
m_pTrafficLightIndexSlider = NULL;
m_pTrafficLightText = NULL;
m_pTrafficLightPickButton = NULL;
m_pTrafficLightClearButton = NULL;
pBank->UnSetCurrentGroup(*m_pTrafficLightLocationsGroup);
//------------------------------------------------
if(m_iCurrentJunction == -1 || m_iCurrentJunction >= m_iNumJunctions)
{
return;
}
CJunctionTemplate & temp = CJunctions::GetJunctionTemplate(m_iCurrentJunction);
if(!(temp.m_iFlags & CJunctionTemplate::Flag_NonEmpty))
{
return;
}
m_iCurrentJunctionIsRailwayCrossing = ((temp.m_iFlags & CJunctionTemplate::Flag_RailwayCrossing)!=0);
m_iCurrentJunctionCanSkipPedPhase = ((temp.m_iFlags & CJunctionTemplate::Flag_DisableSkipPedLightPhase)==0);
m_fCurrentJunctionPhaseOffset = temp.m_fPhaseOffset;
//-----------------------
// Set the phases slider
if( temp.m_iNumPhases == 0 )
{
temp.m_iNumPhases = 1;
temp.m_PhaseTimings[0].m_fStartTime = 0.0f;
temp.m_PhaseTimings[0].m_fDuration = 10.0f;
}
m_iNumPhases = temp.m_iNumPhases;
m_pNumPhasesSlider->SetRange(0, float(temp.m_iNumEntrances));
//---------------------------------
// Create the new entrances groups
char text[256];
pBank->SetCurrentGroup(*m_pEntrancesGroup);
for(e=0; e<temp.m_iNumEntrances; e++)
{
CJunctionTemplate::CEntrance & entrance = temp.m_Entrances[e];
entrance.m_iPhase = Clamp(entrance.m_iPhase, 0, temp.m_iNumPhases-1);
entrance.m_iLeftFilterLanePhase = Clamp(entrance.m_iLeftFilterLanePhase, -1, temp.m_iNumPhases-1);
sprintf(text, "Entrance #%i", e);
m_EntranceBankVars[e].m_pGroup = pBank->PushGroup(text);
m_EntranceBankVars[e].m_pPhaseSlider = pBank->AddSlider("Phase", &entrance.m_iPhase, 0, temp.m_iNumPhases, 1);
m_EntranceBankVars[e].m_pStoppingLineSlider = pBank->AddSlider("Stopping Line", &entrance.m_fStoppingDistance, -20.0f, 20.0f, 0.1f);
m_EntranceBankVars[e].m_pOrientationSlider = pBank->AddSlider("Orientation", &entrance.m_fOrientation, -PI, PI, 0.01f);
m_EntranceBankVars[e].m_pLeftFilterPhaseSlider = pBank->AddSlider("Left Filter Phase", &entrance.m_iLeftFilterLanePhase, -1, temp.m_iNumPhases, 1);
m_EntranceBankVars[e].m_pLeftLaneAheadOnlyToggle = pBank->AddToggle("Left Lane is Ahead Only", &entrance.m_bLeftLaneIsAheadOnly);
m_EntranceBankVars[e].m_pCanTurnRightToggle = pBank->AddToggle("Can Turn Right On Red Light", &entrance.m_bCanTurnRightOnRedLight);
m_EntranceBankVars[e].m_pRightLaneIsRightOnlyToggle = pBank->AddToggle("Right Lane Goes Right Only", &entrance.m_bRightLaneIsRightOnly);
pBank->PopGroup();
}
pBank->UnSetCurrentGroup(*m_pEntrancesGroup);
//------------------------------
// Create the new phases groups
pBank->SetCurrentGroup(*m_pPhasesGroup);
for(p=0; p<temp.m_iNumPhases; p++)
{
sprintf(text, "Phase #%i", p);
m_PhaseVars[p].m_pGroup = pBank->PushGroup(text);
m_PhaseVars[p].m_pTime = pBank->AddSlider("Lights Time", &temp.m_PhaseTimings[p].m_fDuration, 0.0f, 120.0f, 1.0f);
pBank->PopGroup();
}
pBank->UnSetCurrentGroup(*m_pPhasesGroup);
//--------------------------------------
// Create the new traffic lights groups
// Invalidate all entries beyond the number which are defined for this template
// This is because zero seems to be used for a default where parGen doesn't find
// an entry in the xml, and this can't be overridden per data member
for(int t=temp.m_iNumTrafficLightLocations; t<MAX_TRAFFIC_LIGHT_LOCATIONS; t++)
{
temp.m_TrafficLightLocations[t].m_iPosX = TRAFFIC_LIGHT_POS_INVALID;
temp.m_TrafficLightLocations[t].m_iPosY = TRAFFIC_LIGHT_POS_INVALID;
temp.m_TrafficLightLocations[t].m_iPosZ = TRAFFIC_LIGHT_POS_INVALID;
}
m_iCurrentTrafficLight = 0;
SetCurrentTrafficLightText();
pBank->SetCurrentGroup(*m_pTrafficLightLocationsGroup);
m_pTrafficLightSearchDistanceSlider = pBank->AddSlider("Traffic Light Search Distance", &temp.m_fSearchDistance,0.0f,200.0f,1.0f);
m_pNumTrafficLightsText = pBank->AddText("Num Lights Defined : ", m_NumTrafficLightsText, 256 );
m_pTrafficLightIndexSlider = pBank->AddSlider("Lights", &m_iCurrentTrafficLight, 0, MAX_TRAFFIC_LIGHT_LOCATIONS-1, 1, OnChangeCurrentTrafficLight);
m_pTrafficLightText = pBank->AddText("Position:", m_CurrentTrafficLightText, 256);
m_pTrafficLightPickButton = pBank->AddButton("Pick Location", OnPickTrafficLightLocation);
m_pTrafficLightClearButton = pBank->AddButton("Clear", OnClearTrafficLight);
pBank->UnSetCurrentGroup(*m_pTrafficLightLocationsGroup);
}
void CJunctionEditor::NewJunction()
{
if(m_iNumJunctions < CJunctions::GetMaxNumJunctionTemplates())
{
CJunctionTemplate & temp = CJunctions::GetJunctionTemplate(m_iNumJunctions);
temp.m_iFlags = CJunctionTemplate::Flag_NonEmpty;
m_iCurrentJunction = m_iNumJunctions;
m_iDesiredJunction = m_iNumJunctions;
// m_iCurrentEntrance = -1;
m_iNumJunctions++;
}
m_pCurrentJunctionSlider->SetRange(-1.0f, m_iNumJunctions-1.0f);
//InitJunctionWidgets();
m_bMustReinitialiseWidgets = true;
}
void CJunctionEditor::DeleteJunction()
{
if(m_iCurrentJunction >= 0 && m_iCurrentJunction < m_iNumJunctions)
{
CJunctionTemplate & temp = CJunctions::GetJunctionTemplate(m_iCurrentJunction);
temp.m_iFlags = 0;
temp.m_iNumEntrances = 0;
temp.m_iNumJunctionNodes = 0;
temp.m_iNumTrafficLightLocations = 0;
temp.m_iNumPhases = 0;
for(s32 j=0; j<MAX_JUNCTION_NODES_PER_JUNCTION; j++)
{
temp.m_vJunctionNodePositions[j] = g_vInvalidJunctionNodePosition;
}
m_iCurrentJunction = -1;
}
m_pCurrentJunctionSlider->SetRange(-1.0f, m_iNumJunctions-1.0f);
//InitJunctionWidgets();
m_bMustReinitialiseWidgets = true;
}
void CJunctionEditor::StartSelectJunctionNode()
{
if(m_iCurrentJunction != -1)
{
m_iMode = Mode_SelectJunctionNode;
m_bRebindEntrances = false;
}
}
/*
void CJunctionEditor::StartSelectEntranceNode()
{
if(m_iCurrentJunction != -1)
{
m_iMode = Mode_SelectEntranceNode;
m_bRebindEntrances = false;
}
}
*/
void CJunctionEditor::SelectAndRebindJunctionAndEntrances()
{
if(m_iCurrentJunction != -1)
{
m_iMode = Mode_SelectJunctionNode;
m_bRebindEntrances = true;
}
}
void CJunctionEditor::OnSelectJunctionNode(const CNodeAddress & iNodeAddress)
{
Assert(m_iCurrentJunction >= 0 && m_iCurrentJunction < m_iNumJunctions);
Assert(!iNodeAddress.IsEmpty());
//----------------------------------------------------------------------------
// If we have a junction node selected, then set this in the current junction
const CPathNode * pNode = ThePaths.FindNodePointerSafe(iNodeAddress);
Assert(pNode);
Vector3 vCoords;
pNode->GetCoors(vCoords);
//----------------------------------------------
// Ensure we don't already have a template here
CJunctionTemplate * pTemp = CJunctions::GetJunctionTemplateAtPosition(vCoords);
if(!pTemp || m_bRebindEntrances)
{
CJunctionTemplate & currentJunction = CJunctions::GetJunctionTemplate(m_iCurrentJunction);
// Flag as in-use
currentJunction.m_iFlags |= CJunctionTemplate::Flag_NonEmpty;
currentJunction.m_vJunctionMin = vCoords;
currentJunction.m_vJunctionMin.z -= 2.0f;
currentJunction.m_vJunctionMax = vCoords;
currentJunction.m_vJunctionMax.z += 8.0f;
s32 iNumJunctionNodes = 0;
const CPathNode * ppJunctionNodes[MAX_JUNCTION_NODES_PER_JUNCTION];
GetJunctionsNodesLinkedToJunctionNode(pNode, ppJunctionNodes, MAX_JUNCTION_NODES_PER_JUNCTION, iNumJunctionNodes);
currentJunction.m_iNumJunctionNodes = iNumJunctionNodes;
for(s32 n=0; n<iNumJunctionNodes; n++)
{
ppJunctionNodes[n]->GetCoors(vCoords);
currentJunction.m_vJunctionNodePositions[n] = vCoords;
}
s32 iNumEntrances = 0;
const CPathNode * ppEntranceNodes[MAX_ROADS_INTO_JUNCTION];
GetEntrancesLinkedToJunctions(ppJunctionNodes, iNumJunctionNodes, ppEntranceNodes, MAX_ROADS_INTO_JUNCTION, iNumEntrances);
currentJunction.m_iNumEntrances = iNumEntrances;
s32 e;
for(e=0; e<iNumEntrances; e++)
{
Vector3 vEntrancePos;
ppEntranceNodes[e]->GetCoors(vEntrancePos);
currentJunction.m_vJunctionMin.x = Min(currentJunction.m_vJunctionMin.x, vEntrancePos.x);
currentJunction.m_vJunctionMin.y = Min(currentJunction.m_vJunctionMin.y, vEntrancePos.y);
currentJunction.m_vJunctionMin.z = Min(currentJunction.m_vJunctionMin.z, vEntrancePos.z);
currentJunction.m_vJunctionMax.x = Max(currentJunction.m_vJunctionMax.x, vEntrancePos.x);
currentJunction.m_vJunctionMax.y = Max(currentJunction.m_vJunctionMax.y, vEntrancePos.y);
currentJunction.m_vJunctionMax.z = Max(currentJunction.m_vJunctionMax.z, vEntrancePos.z);
currentJunction.m_Entrances[e].m_vNodePosition = vEntrancePos;
currentJunction.m_Entrances[e].m_fStoppingDistance = 0.0f;
// Try to approximate a junction orientation
CNodeAddress iEntranceNode = ThePaths.FindNodeClosestToCoors(vEntrancePos, PathfindFindNonJunctionNodeCB, NULL, 2.0f);
Assert(!iEntranceNode.IsEmpty());
const CPathNode * pEntranceNode = ThePaths.FindNodePointerSafe(iEntranceNode);
Assert(pEntranceNode && !pEntranceNode->IsJunctionNode());
if(pEntranceNode)
{
Vector3 vLinkedNodePos;
pEntranceNode->GetCoors(vLinkedNodePos);
Vector3 vPrevNodePos = vEntrancePos;
if(pEntranceNode->NumLinks()==2)
{
const CPathNode * pLinkedNode1 = ThePaths.GetNodesLinkedNode(pEntranceNode, 0);
const CPathNode * pLinkedNode2 = ThePaths.GetNodesLinkedNode(pEntranceNode, 1);
const CPathNode * pPrevNode = (pLinkedNode1 && pLinkedNode2) ?
(pLinkedNode1->IsJunctionNode() ? pLinkedNode2 : pLinkedNode1) : NULL;
if(pPrevNode)
pPrevNode->GetCoors(vPrevNodePos);
}
Vector3 vEntranceDir = vPrevNodePos - vLinkedNodePos;
currentJunction.m_Entrances[e].m_fOrientation = rage::Atan2f(-vEntranceDir.x, vEntranceDir.y);
currentJunction.m_Entrances[e].m_fOrientation = fwAngle::LimitRadianAngleSafe(currentJunction.m_Entrances[e].m_fOrientation);
}
else
{
currentJunction.m_Entrances[e].m_fOrientation = 0.0f;
}
// Get the angle used for sorting in clockwise order
currentJunction.m_Entrances[e].m_fAngleFromCenter = fwAngle::GetRadianAngleBetweenPoints(vEntrancePos.x, vEntrancePos.y, vCoords.x, vCoords.y);
if(currentJunction.m_Entrances[e].m_fAngleFromCenter < 0.0f)
currentJunction.m_Entrances[e].m_fAngleFromCenter += TWO_PI;
if(currentJunction.m_Entrances[e].m_fAngleFromCenter >= TWO_PI)
currentJunction.m_Entrances[e].m_fAngleFromCenter -= TWO_PI;
}
// m_iCurrentEntrance = 0;
// m_pCurrentEntranceSlider->SetRange(-1.0f, iNumEntrances-1.0f);
currentJunction.m_iNumPhases = iNumEntrances;
for(s32 p=0; p<currentJunction.m_iNumPhases; p++)
{
currentJunction.m_PhaseTimings[p].m_fDuration = 10.0f;
}
//------------------------------------------------------------
// Now sort all the entrances so they are in clockwise order
bool bChange = true;
while(bChange)
{
bChange = false;
for(e = 0; e < iNumEntrances-1; e++)
{
if(currentJunction.m_Entrances[e].m_fAngleFromCenter > currentJunction.m_Entrances[e+1].m_fAngleFromCenter)
{
CJunctionTemplate::CEntrance temp = currentJunction.m_Entrances[e];
currentJunction.m_Entrances[e] = currentJunction.m_Entrances[e+1];
currentJunction.m_Entrances[e+1] = temp;
bChange = true;
}
}
}
for(e=0; e<iNumEntrances; e++)
{
currentJunction.m_Entrances[e].m_iPhase = e;
}
}
// Init widgets
//InitJunctionWidgets();
m_bMustReinitialiseWidgets = true;
}
void GetJunctionsNodesLinkedToJunctionNode(const CPathNode * pNode, const CPathNode ** ppLinkedNodes, const s32 iMaxLinkedNodes, s32 & iNumLinkedNodes)
{
Assert(pNode->m_2.m_qualifiesAsJunction || CJunctionEditor::m_bSelectAnyNodeAsJunction);
if(iNumLinkedNodes >= iMaxLinkedNodes)
return;
ppLinkedNodes[iNumLinkedNodes++] = pNode;
for(s32 l=0; l<pNode->NumLinks(); l++)
{
const CPathNodeLink & link = ThePaths.GetNodesLink(pNode, l);
if(link.m_1.m_bShortCut)
continue;
const CPathNode * pLinkedNode = ThePaths.GetNodesLinkedNode(pNode, l);
if(pLinkedNode && pLinkedNode->m_2.m_qualifiesAsJunction)
{
// Is this node already in the list?..
s32 m;
for(m=0; m<iNumLinkedNodes; m++)
{
if(ppLinkedNodes[m]==pLinkedNode)
break;
}
// ..if not then visit it now
if(m==iNumLinkedNodes)
{
GetJunctionsNodesLinkedToJunctionNode(pLinkedNode, ppLinkedNodes, iMaxLinkedNodes, iNumLinkedNodes);
}
}
}
}
void GetEntrancesLinkedToJunctions(const CPathNode ** ppJunctionNodes, const s32 iNumJunctionNodes, const CPathNode ** ppEntranceNodes, const s32 iMaxNumEntranceNodes, s32 & iNumEntranceNodes)
{
for(s32 j=0; j<iNumJunctionNodes; j++)
{
const CPathNode * pJunctionNode = ppJunctionNodes[j];
for(s32 l=0; l<pJunctionNode->NumLinks(); l++)
{
const CPathNodeLink & link = ThePaths.GetNodesLink(pJunctionNode, l);
// Don't include shortcuts as entrances
if(link.m_1.m_bShortCut)
continue;
// Ignore links which are exit-only
if(link.m_1.m_LanesFromOtherNode == 0)
continue;
const CPathNode * pLinkedNode = ThePaths.GetNodesLinkedNode(pJunctionNode, l);
if(pLinkedNode && !pLinkedNode->m_2.m_qualifiesAsJunction)
{
s32 e;
for(e=0; e<iNumEntranceNodes; e++)
{
if(ppEntranceNodes[e]==pLinkedNode)
break;
}
if(e==iNumEntranceNodes)
{
ppEntranceNodes[iNumEntranceNodes++] = pLinkedNode;
if(iNumEntranceNodes >= iMaxNumEntranceNodes)
return;
}
}
}
}
}
void CJunctionEditor::UpdateSelectTrafficLightLocation()
{
if(CDebugScene::GetMouseRightPressed())
{
m_iMode = Mode_Normal;
return;
}
//-------------------------------------------------------------------
// Draw the intersection pos with geometry of ray cast through mouse
Vector3 vWorldPos;
const bool bHit = CDebugScene::GetWorldPositionUnderMouse(vWorldPos);
if(bHit)
{
s16 posX = (s16)(vWorldPos.x+0.5f);
s16 posY = (s16)(vWorldPos.y+0.5f);
s16 posZ = (s16)(vWorldPos.z+0.5f);
grcDebugDraw::Sphere(Vector3((float)posX,(float)posY,(float)posZ), 6.0f, Color_SpringGreen2, false);
if(CDebugScene::GetMouseLeftPressed())
{
CJunctionTemplate & temp = CJunctions::GetJunctionTemplate(m_iCurrentJunction);
CJunctionTemplate::CTrafficLightLocation & lightLoc = temp.m_TrafficLightLocations[m_iCurrentTrafficLight];
bool bNewEntry = lightLoc.m_iPosX == TRAFFIC_LIGHT_POS_INVALID;
lightLoc.m_iPosX = posX;
lightLoc.m_iPosY = posY;
lightLoc.m_iPosZ = posZ;
// only increment the num entries if we've just filled in a new slot
if(bNewEntry)
temp.m_iNumTrafficLightLocations++;
Assert(temp.m_iNumTrafficLightLocations <= MAX_TRAFFIC_LIGHT_LOCATIONS);
m_iCurrentTrafficLight++;
m_iCurrentTrafficLight = Min(m_iCurrentTrafficLight, temp.m_iNumTrafficLightLocations);
m_iCurrentTrafficLight = Min(m_iCurrentTrafficLight, MAX_TRAFFIC_LIGHT_LOCATIONS-1);
SetCurrentTrafficLightText();
m_iMode = Mode_Normal;
}
}
}
void CJunctionEditor::SetCurrentTrafficLightText()
{
CJunctionTemplate & temp = CJunctions::GetJunctionTemplate(m_iCurrentJunction);
CJunctionTemplate::CTrafficLightLocation & lightLoc = temp.m_TrafficLightLocations[m_iCurrentTrafficLight];
if(lightLoc.m_iPosX == TRAFFIC_LIGHT_POS_INVALID)
{
strcpy(m_CurrentTrafficLightText, "[none]");
}
else
{
sprintf(m_CurrentTrafficLightText, "[%i, %i, %i]", lightLoc.m_iPosX, lightLoc.m_iPosY, lightLoc.m_iPosZ);
}
if(m_pTrafficLightText)
m_pTrafficLightText->SetString(m_CurrentTrafficLightText);
sprintf(m_NumTrafficLightsText, "%i", temp.m_iNumTrafficLightLocations);
if(m_pNumTrafficLightsText)
m_pNumTrafficLightsText->SetString(m_NumTrafficLightsText);
}
void CJunctionEditor::FindOrCreateAutoAdjustment()
{
CJunction* junction = CJunctions::Debug_GetClosestJunction();
if (junction )
{
if (junction->HasTrafficLightNodes())
{
m_CurrentAutoJuncAdj = CJunctions::FindOrCreateAutoJunctionAdjustment(*junction);
}
else
{
Warningf("No traffic lights at this junction");
}
}
else
{
m_CurrentAutoJuncAdj.m_vLocation.ZeroComponents();
m_CurrentAutoJuncAdj.m_fCycleDuration = 0.0f;
m_CurrentAutoJuncAdj.m_fCycleOffset = 0.0f;
}
}
void CJunctionEditor::RemoveAutoAdjustment()
{
CJunction* junction = CJunctions::Debug_GetClosestJunction();
if (junction)
{
CJunctions::DeleteAutoJunctionAdjustment(*junction);
}
}
void CJunctionEditor::OnUpdateAutoAdjustment()
{
CAutoJunctionAdjustment* adj = CJunctions::FindAutoJunctionAdjustmentData(m_CurrentAutoJuncAdj.m_vLocation);
*adj = m_CurrentAutoJuncAdj;
}
bool JunctionEditorJunctionNodeCB(CPathNode * pNode, void * UNUSED_PARAM(pData))
{
if(pNode->IsWaterNode() || pNode->IsPedNode() || pNode->IsParkingNode() || pNode->IsOpenSpaceNode())
return false;
if(!pNode->IsJunctionNode() && !CJunctionEditor::m_bSelectAnyNodeAsJunction)
return false;
return true;
}
bool JunctionEditorFindNonJunctionNodeCB(CPathNode * pNode, void * UNUSED_PARAM(pData))
{
if(pNode->IsWaterNode() || pNode->IsPedNode() || pNode->IsParkingNode() || pNode->IsOpenSpaceNode())
return false;
if(pNode->IsJunctionNode())
return false;
return true;
}
void CJunctionEditor::UpdateSelectNode()
{
if(CDebugScene::GetMouseRightPressed())
{
m_iMode = Mode_Normal;
return;
}
const Vector3 vRaiseJunction(0.0f,0.0f,5.0f);
//-------------------------------------------------------------------
// Draw the intersection pos with geometry of ray cast through mouse
Vector3 vWorldPos;
const bool bHit = CDebugScene::GetWorldPositionUnderMouse(vWorldPos);
if(bHit)
{
grcDebugDraw::Sphere(vWorldPos, 2.0f, Color_orange, false);
if(m_iMode == Mode_SelectJunctionNode)
{
//-----------------------------------------------------------------------
// Draw highlighted junction node if there is one close to intersect pos
CNodeAddress iCloseNode = ThePaths.FindNodeClosestToCoors(vWorldPos, JunctionEditorJunctionNodeCB, NULL, 2.0f);
const CPathNode * pCloseNode = ThePaths.FindNodePointerSafe(iCloseNode);
if(pCloseNode &&
(pCloseNode->m_2.m_qualifiesAsJunction || m_bSelectAnyNodeAsJunction))
{
s32 iNumLinkedNodes = 0;
const CPathNode * ppLinkedNodes[MAX_JUNCTION_NODES_PER_JUNCTION];
GetJunctionsNodesLinkedToJunctionNode(pCloseNode, ppLinkedNodes, MAX_JUNCTION_NODES_PER_JUNCTION, iNumLinkedNodes);
for(s32 n=0; n<iNumLinkedNodes; n++)
{
pCloseNode = ppLinkedNodes[n];
Vector3 vJunctionCoords;
pCloseNode->GetCoors(vJunctionCoords);
grcDebugDraw::Cylinder(RCC_VEC3V(vJunctionCoords), VECTOR3_TO_VEC3V(vJunctionCoords+vRaiseJunction), 1.5f, Color_purple);
}
if(CDebugScene::GetMouseLeftPressed())
{
OnSelectJunctionNode(iCloseNode);
m_iMode = Mode_Normal;
}
}
}
/*
else if(m_iMode == Mode_SelectEntranceNode)
{
//-----------------------------------------------------------------------
// Draw highlighted entrance node if there is one close to intersect pos
Assert(m_iCurrentJunction != -1);
CJunctionTemplate & temp = CJunctions::GetJunctionTemplate(m_iCurrentJunction);
float fClosest = FLT_MAX;
int iClosestEntrance = -1;
for(int e=0; e<temp.m_iNumEntrances; e++)
{
CJunctionTemplate::CEntrance & entance = temp.m_Entrances[e];
if((entance.m_vNodePosition - vWorldPos).XYMag() < fClosest)
{
fClosest = (entance.m_vNodePosition - vWorldPos).XYMag();
iClosestEntrance = e;
}
}
if(iClosestEntrance != -1)
{
CJunctionTemplate::CEntrance & entance = temp.m_Entrances[iClosestEntrance];
grcDebugDraw::Cylinder(entance.m_vNodePosition, entance.m_vNodePosition+vRaiseLink, 1.5f, Color_purple, false, false);
if(CDebugScene::GetMouseLeftPressed())
{
m_iCurrentEntrance = iClosestEntrance;
m_iMode = Mode_Normal;
}
}
}
*/
}
}
void CJunctionEditor::SetInitialNodesDebug()
{
//-------------------------------------
// Set pathfind node/links debugging
ThePaths.bDisplayPathsDebug_Allow = true;
ThePaths.bDisplayPathsDebug_Nodes_AllowDebugDraw = true;
ThePaths.bDisplayPathsDebug_Nodes_StandardInfo = true; //false;
ThePaths.bDisplayPathsDebug_Nodes_Pilons = true;
ThePaths.bDisplayPathsDebug_Nodes_ToCollisionDiff = false;
ThePaths.bDisplayPathsDebug_Nodes_StreetNames = false;
ThePaths.bDisplayPathsDebug_Nodes_DistanceHash = false;
ThePaths.bDisplayPathsDebug_Links_AllowDebugDraw = true;
ThePaths.bDisplayPathsDebug_Links_DrawLine = true;
ThePaths.bDisplayPathsDebug_Links_DrawLaneDirectionArrows = true;
ThePaths.bDisplayPathsDebug_Links_DrawTrafficLigths = true;
ThePaths.bDisplayPathsDebug_Links_TextInfo = false;
ThePaths.bDisplayPathsDebug_Links_LaneCenters = false;
ThePaths.bDisplayPathsDebug_Links_RoadExtremes = false;
ThePaths.bDisplayPathsDebug_Links_Tilt = false;
ThePaths.bDisplayPathsDebug_Links_RegionAndIndex = false;
}
void CJunctionEditor::Render()
{
if(!m_bActivated || !m_bShowEditor)
return;
//------------------------------
// Display HUD text
RenderTextHUD();
camDebugDirector & debugDirector = camInterface::GetDebugDirector();
const Vector3 vOrigin = debugDirector.IsFreeCamActive() ? debugDirector.GetFreeCamFrame().GetPosition() : CPlayerInfo::ms_cachedMainPlayerPos;
RenderJunctionNodes(vOrigin);
for(int t=0; t<m_iNumJunctions; t++)
{
CJunctionTemplate & temp = CJunctions::GetJunctionTemplate(t);
if((temp.m_iFlags & CJunctionTemplate::Flag_NonEmpty)==0)
continue;
Color32 iBoxCol = (t == m_iCurrentJunction) ? Color_turquoise : Color_OrangeRed;
// If we had an error binding this junction, then flash it red as a warning
bool bErrorBindingJunction = false;
if(temp.m_iNumJunctionNodes > 0)
{
s32 iJunction = CJunctions::GetJunctionAtPosition(temp.m_vJunctionNodePositions[0]);
if(iJunction != -1)
{
CJunction * pJunction = CJunctions::GetJunctionByIndex(iJunction);
if(pJunction && pJunction->GetErrorBindingJunction() && (fwTimer::GetFrameCount() & 1))
{
iBoxCol = Color_red;
bErrorBindingJunction = true;
}
}
}
grcDebugDraw::BoxAxisAligned(RCC_VEC3V(temp.m_vJunctionMin), RCC_VEC3V(temp.m_vJunctionMax), iBoxCol, false);
char tmp[128];
if(bErrorBindingJunction)
{
sprintf(tmp, "ERROR BINDING JUNCTION TEMPLATE : %i", t);
grcDebugDraw::Text((temp.m_vJunctionMin+temp.m_vJunctionMax)*0.5f, iBoxCol, tmp);
}
else
{
sprintf(tmp, "Junction Template: %i", t);
grcDebugDraw::Text((temp.m_vJunctionMin+temp.m_vJunctionMax)*0.5f, iBoxCol, tmp);
}
}
if(m_iCurrentJunction >= 0 && m_iCurrentJunction < m_iNumJunctions)
{
RenderJunctionTemplate(vOrigin, m_iCurrentJunction);
}
if(m_iSecondaryJunction >= 0 && m_iSecondaryJunction < m_iNumJunctions)
{
RenderJunctionTemplate(vOrigin, m_iSecondaryJunction);
}
}
void CJunctionEditor::RenderJunctionTemplate(const Vector3 & vOrigin, const s32 iTemplate)
{
CJunctionTemplate & temp = CJunctions::GetJunctionTemplate(iTemplate);
if(!(temp.m_iFlags & CJunctionTemplate::Flag_NonEmpty))
return;
CNodeAddress iCentralNode = ThePaths.FindNodeClosestToCoors(temp.m_vJunctionNodePositions[0], PathfindFindJunctionNodeCB, NULL, 2.0f);
if(iCentralNode.IsEmpty())
return;
const CPathNode * pJunctionNode = ThePaths.FindNodePointerSafe(iCentralNode);
if(!pJunctionNode)
return;
Assert(pJunctionNode->m_2.m_qualifiesAsJunction);
Vector3 vJunctionPos, vLinkedNodePos;
pJunctionNode->GetCoors(vJunctionPos);
const float fMaxDistSqr = 100.0f * 100.0f;
const float fDist2 = (vJunctionPos - vOrigin).Mag2();
if(fDist2 > fMaxDistSqr)
return;
const Vector3 vRaiseJunction(0.0f,0.0f,5.0f);
const Vector3 vRaiseLink(0.0f,0.0f,3.0f);
const Vector3 vRaiseStopLine(0.0f,0.0f,1.0f);
const Vector3 vTextRaise(0.0f,0.0f,1.0f); //2.0f);
const s32 iTextHeight = grcDebugDraw::GetScreenSpaceTextHeight();
char text[256];
int iScrY = 0;
sprintf(text, "Template (%i)", iTemplate);
grcDebugDraw::Text(vJunctionPos+vTextRaise, Color_red, 0, 0, text);
iScrY += grcDebugDraw::GetScreenSpaceTextHeight();
// Print out some info about this junction if it is active
int iJunctionIndex = CJunctions::GetJunctionUsingTemplate(iTemplate);
if(iJunctionIndex != -1)
{
CJunction * pJunction = CJunctions::GetJunctionByIndex(iJunctionIndex);
int iCurrPhase = pJunction->GetLightPhase();
sprintf(text, "Current Light Phase : %i / %i", iCurrPhase, pJunction->GetNumLightPhases() );
grcDebugDraw::Text(vJunctionPos+vTextRaise, Color_red, 0, iScrY, text);
iScrY += grcDebugDraw::GetScreenSpaceTextHeight();
sprintf(text, "Phase Time : %.1f / %.1f", pJunction->GetLightPhaseDuration(iCurrPhase)-pJunction->GetLightTimeRemaining(), pJunction->GetLightPhaseDuration(iCurrPhase));
grcDebugDraw::Text(vJunctionPos+vTextRaise, Color_red, 0, iScrY, text);
iScrY += grcDebugDraw::GetScreenSpaceTextHeight();
sprintf(text, "Active entrances:");
grcDebugDraw::Text(vJunctionPos+vTextRaise, Color_red, 0, iScrY, text);
iScrY += grcDebugDraw::GetScreenSpaceTextHeight();
int iActiveEntrances = 0;
// Display all the currently active entrances
for(int e=0; e<pJunction->GetNumEntrances(); e++)
{
CJunctionEntrance & entrance = pJunction->GetEntrance(e);
if(entrance.m_iPhase == iCurrPhase && entrance.m_iLeftFilterPhase != iCurrPhase)
{
iActiveEntrances++;
sprintf(text, "%i", e);
grcDebugDraw::Text(vJunctionPos+vTextRaise, Color_red, 16, iScrY, text);
iScrY += grcDebugDraw::GetScreenSpaceTextHeight();
}
if(entrance.m_iLeftFilterPhase == iCurrPhase)
{
iActiveEntrances++;
sprintf(text, "%i (filter left)", e);
grcDebugDraw::Text(vJunctionPos+vTextRaise, Color_red, 16, iScrY, text);
iScrY += grcDebugDraw::GetScreenSpaceTextHeight();
}
}
if(iActiveEntrances == 0)
{
sprintf(text, "none (peds will cross)");
grcDebugDraw::Text(vJunctionPos+vTextRaise, Color_red, 16, iScrY, text);
iScrY += grcDebugDraw::GetScreenSpaceTextHeight();
}
// Determine whether this junction has any light phases w/o any entrances assigned
// This is the prerequisite for peds to cross
int l,e;
for(l=0; l<pJunction->GetNumLightPhases(); l++)
{
// Display all the currently active entrances
for(e=0; e<pJunction->GetNumEntrances(); e++)
{
CJunctionEntrance & entrance = pJunction->GetEntrance(e);
if(entrance.m_iPhase == l || entrance.m_iLeftFilterPhase == l)
break;
}
if(e == pJunction->GetNumEntrances()) // Light phase 'l has no entrances assigned & will be used for peds
break;
}
if(l==pJunction->GetNumLightPhases())
{
sprintf(text, "All light phases assigned - no phase for peds to cross!");
grcDebugDraw::Text(vJunctionPos+vTextRaise, Color_red, 16, iScrY, text);
iScrY += grcDebugDraw::GetScreenSpaceTextHeight();
}
if(pJunction->GetIsRailwayCrossing())
{
if(pJunction->GetRailwayBarriersShouldBeDown())
{
sprintf(text, "Railway crossing : train approaching!");
grcDebugDraw::Text(vJunctionPos+vTextRaise, Color_red, 16, iScrY, text);
iScrY += grcDebugDraw::GetScreenSpaceTextHeight();
}
else
{
sprintf(text, "Railway crossing : idle");
grcDebugDraw::Text(vJunctionPos+vTextRaise, Color_red, 16, iScrY, text);
iScrY += grcDebugDraw::GetScreenSpaceTextHeight();
}
}
}
else
{
sprintf(text, "Junction not created");
grcDebugDraw::Text(vJunctionPos+vTextRaise, Color_red, 0, grcDebugDraw::GetScreenSpaceTextHeight(), text);
}
s32 iNumJunctionNodes = 0;
const CPathNode * ppJunctionNodes[MAX_JUNCTION_NODES_PER_JUNCTION];
GetJunctionsNodesLinkedToJunctionNode(pJunctionNode, ppJunctionNodes, MAX_JUNCTION_NODES_PER_JUNCTION, iNumJunctionNodes);
for(s32 j=0; j<iNumJunctionNodes; j++)
{
ppJunctionNodes[j]->GetCoors(vJunctionPos);
grcDebugDraw::Cylinder(RCC_VEC3V(vJunctionPos), VECTOR3_TO_VEC3V(vJunctionPos+vRaiseJunction), 1.0f, Color_cyan);
}
for(s32 e=0; e<temp.m_iNumEntrances; e++)
{
CJunctionTemplate::CEntrance & entrance = temp.m_Entrances[e];
CNodeAddress iEntranceNode = ThePaths.FindNodeClosestToCoors(entrance.m_vNodePosition, PathfindFindNonJunctionNodeCB, NULL, 2.0f);
Assert(!iEntranceNode.IsEmpty());
const CPathNode * pEntranceNode = ThePaths.FindNodePointerSafe(iEntranceNode);
Assert(pEntranceNode && !pEntranceNode->IsJunctionNode());
if(pEntranceNode)
{
//-----------------------------------------------------------------------------------
// Draw some info about the road node - num lanes, etc
// This is so we can differentiate between nodes which might look almost on top of
// each other due to the way the slip-lanes have to run coincident..
int iNumLanesToJunction, iNumLanesFromJunction;
GetNumLanesForEntranceNode(pEntranceNode, iNumLanesToJunction, iNumLanesFromJunction);
Color32 iCylCol = (iNumLanesToJunction == 1 && iNumLanesFromJunction == 0) ? Color_cyan4 : Color_cyan;
pEntranceNode->GetCoors(vLinkedNodePos);
grcDebugDraw::Cylinder(RCC_VEC3V(entrance.m_vNodePosition), VECTOR3_TO_VEC3V(entrance.m_vNodePosition+vRaiseLink), 0.75f, iCylCol);
sprintf(text, "Entrance (%i)", e);
grcDebugDraw::Text(vLinkedNodePos+vTextRaise, Color_orange, 0, 0, text);
sprintf(text, "NumLanesToJunction: %i", iNumLanesToJunction);
grcDebugDraw::Text(vLinkedNodePos+vTextRaise, Color_orange2, 0, iTextHeight, text);
sprintf(text, "NumLanesFromJunction: %i", iNumLanesFromJunction);
grcDebugDraw::Text(vLinkedNodePos+vTextRaise, Color_orange2, 0, iTextHeight*2, text);
sprintf(text, "Phase : %i", entrance.m_iPhase);
grcDebugDraw::Text(vLinkedNodePos+vTextRaise, Color_orange, 0, iTextHeight*3, text);
sprintf(text, "Filter Left on Phase: %i", entrance.m_iLeftFilterLanePhase);
grcDebugDraw::Text(vLinkedNodePos+vTextRaise, Color_orange, 0, iTextHeight*4, text);
sprintf(text, "Filter Right : %s", entrance.m_bCanTurnRightOnRedLight ? "true":"false");
grcDebugDraw::Text(vLinkedNodePos+vTextRaise, Color_orange, 0, iTextHeight*5, text);
sprintf(text, "Left Lane Ahead Only : %s", entrance.m_bLeftLaneIsAheadOnly ? "true":"false");
grcDebugDraw::Text(vLinkedNodePos+vTextRaise, Color_orange, 0, iTextHeight*6, text);
sprintf(text, "Right Lane Right Only : %s", entrance.m_bRightLaneIsRightOnly ? "true" : "false");
grcDebugDraw::Text(vLinkedNodePos+vTextRaise, Color_orange, 0, iTextHeight*7, text);
//----------------------------------------------------------------------
// Draw the stopping lines
// This is complicated because we want to use the orientation from the
// previous node to each entrance node, approaching the junction.
Vector3 vEntranceDir;
vEntranceDir.x = -rage::Sinf(entrance.m_fOrientation);
vEntranceDir.y = rage::Cosf(entrance.m_fOrientation);
vEntranceDir.z = 0.0f;
vEntranceDir.Normalize();
Vector3 vRight;
vRight.Cross(vEntranceDir, ZAXIS);
vRight.Normalize();
vRight *= 5.0f;
const Vector3 vStopMid = vLinkedNodePos + (vEntranceDir * entrance.m_fStoppingDistance) + vRaiseStopLine;
grcDebugDraw::Line(RCC_VEC3V(vStopMid), RCC_VEC3V(vLinkedNodePos), Color_orange2, Color_orange4);
grcDebugDraw::Line(VECTOR3_TO_VEC3V(vStopMid - vRight), VECTOR3_TO_VEC3V(vStopMid + vRight), Color_orange2, Color_orange2);
grcDebugDraw::Arrow(RCC_VEC3V(vStopMid), VECTOR3_TO_VEC3V(vStopMid + vEntranceDir), 0.25f, Color_orange);
}
else
{
grcDebugDraw::Cylinder(RCC_VEC3V(entrance.m_vNodePosition), VECTOR3_TO_VEC3V(entrance.m_vNodePosition+vRaiseLink), 0.75f, Color_red);
}
}
pJunctionNode->GetCoors(vJunctionPos);
grcDebugDraw::Sphere(vJunctionPos,temp.m_fSearchDistance,Color32(255,0,0,64),false);
for(s32 l=0; l<temp.m_iNumTrafficLightLocations; l++)
{
Vector3 vPos;
temp.m_TrafficLightLocations[l].GetAsVec3(vPos);
Color32 col = (m_iCurrentTrafficLight == l) ? Color_LightSeaGreen : Color_SeaGreen4;
grcDebugDraw::Sphere(vPos, 6.0f, col, false);
}
}
//-----------------------------------------------------------------
// RenderJunctionNodes
// Draws nodes in the world which have the Junction flag set.
// These are the nodes which may be selected to form the central
// point of a hand-edited junction.
void CJunctionEditor::RenderJunctionNodes(const Vector3 & vOrigin)
{
if(!m_bActivated)
return;
s32 r,n,l;
Vector3 vNodePos, vLinkedNodePos;
for(r=0; r<PATHFINDREGIONS; r++)
{
if(ThePaths.IsRegionLoaded(r))
{
const s32 iNumNodes = ThePaths.apRegions[r]->NumNodes;
for(n=0; n<iNumNodes; n++)
{
CPathNode & node = ThePaths.apRegions[r]->aNodes[n];
if(node.m_2.m_qualifiesAsJunction || m_bSelectAnyNodeAsJunction)
{
node.GetCoors(vNodePos);
const float fDist2 = (vNodePos - vOrigin).Mag2();
if(fDist2 < fMaxDistSqr)
{
// Is this the current junction?
CJunctionTemplate * pCurrentTemplate = NULL;
CJunctionTemplate * pTemplate = CJunctions::GetJunctionTemplateAtPosition(vNodePos);
if(m_iCurrentJunction >= 0)
{
pCurrentTemplate = &CJunctions::GetJunctionTemplate(m_iCurrentJunction);
}
bool bCurrentJunction = pCurrentTemplate && (pCurrentTemplate==pTemplate);
Color32 iCol = iJunctionCol;
if(!bCurrentJunction)
iCol.SetAlpha(128);
grcDebugDraw::Cylinder(RCC_VEC3V(vNodePos), VECTOR3_TO_VEC3V(vNodePos+vRaiseJunction), 1.0f, iCol, false, false);
for(l=0; l<node.NumLinks(); l++)
{
const CPathNode * pLinkedNode = ThePaths.GetNodesLinkedNode(&node, l);
if(pLinkedNode)
{
pLinkedNode->GetCoors(vLinkedNodePos);
if(!pLinkedNode->m_2.m_qualifiesAsJunction)
{
// Is this a slip lane?
// Identifiable by having one lane to the junction, and none from
// If so we'll colour it differently to differentiate
iCol = iEntranceCol;
const CPathNodeLink & link = ThePaths.GetNodesLink(&node, l);
if(link.m_1.m_LanesToOtherNode == 1 && link.m_1.m_LanesFromOtherNode == 0)
{
iCol = iEntranceColSlipLane;
}
if(!bCurrentJunction)
iCol.SetAlpha(128);
grcDebugDraw::Cylinder(RCC_VEC3V(vLinkedNodePos), VECTOR3_TO_VEC3V(vLinkedNodePos+vRaiseLink), 0.75f, iCol, false, false);
}
iCol = iLinkCol;
int iLinkAlpha = bCurrentJunction ? 64 : 32;
iCol.SetAlpha(iLinkAlpha);
grcDebugDraw::Cylinder(VECTOR3_TO_VEC3V(vNodePos+vRaiseJoin), VECTOR3_TO_VEC3V(vLinkedNodePos+vRaiseJoin), 0.25f, iCol, false, false, 6);
}
}
}
}
}
}
}
}
//-----------------------------------------------------------------------------------
// Draw some info about the road node - num lanes, etc
// This is so we can differentiate between nodes which might look almost on top of
// each other due to the way the slip-lanes have to run coincident..
bool CJunctionEditor::GetNumLanesForEntranceNode(const CPathNode * pEntranceNode, int & iNumLanesToJunction, int & iNumLanesFromJunction)
{
iNumLanesToJunction = 0;
iNumLanesFromJunction = 0;
if(pEntranceNode)
{
for(int l=0; l<pEntranceNode->NumLinks(); l++)
{
CPathNodeLink link = ThePaths.GetNodesLink(pEntranceNode, l);
const CPathNode * pAdjNode = ThePaths.GetNodesLinkedNode(pEntranceNode, l);
if(pAdjNode && pAdjNode->IsJunctionNode())
{
iNumLanesToJunction = link.m_1.m_LanesToOtherNode;
iNumLanesFromJunction = link.m_1.m_LanesFromOtherNode;
return true;
}
}
}
return false;
}
void CJunctionEditor::RenderTextHUD()
{
char text[128];
grcDebugDraw::Text( Vector2(0.05f,0.05f), Color_yellow, "Junction Editor", false, 4, 4);
// Display which junction template is currently selected
if(m_iCurrentJunction == -1)
{
sprintf(text, "Current template : none");
return;
}
CJunctionTemplate & temp = CJunctions::GetJunctionTemplate(m_iCurrentJunction);
if( (temp.m_iFlags & CJunctionTemplate::Flag_NonEmpty) == 0)
{
sprintf(text, "Current template : %i [EMPTY]", m_iCurrentJunction);
}
else if( temp.m_iNumJunctionNodes==0 || temp.m_vJunctionNodePositions[0].IsClose(g_vInvalidJunctionNodePosition, 0.1f) )
{
sprintf(text, "Current template : %i [JUNCTION NODE NOT YET SELECTED]", m_iCurrentJunction);
}
else
{
sprintf(text, "Current template : %i", m_iCurrentJunction);
}
grcDebugDraw::Text( Vector2(0.05f,0.1f), Color_yellow, text, false, 2, 2);
float fScale = 2.0f;
float fYPos = 0.15f;
//float fTextHeight = (float)grcDebugDraw::GetScreenSpaceTextHeight() / (float)grcDevice::GetHeight();
//float fYInc = fTextHeight * fScale;
// Info specific to editing a junction
if(m_iCurrentJunction != -1)
{
// If we are waiting to select a junction node
if(m_iMode == Mode_SelectJunctionNode)
{
sprintf(text, "Select junction node (right-click to cancel)");
grcDebugDraw::Text( Vector2(0.05f,fYPos), Color_green, text, false, fScale, fScale);
}
/*
else if(m_iMode == Mode_SelectEntranceNode)
{
sprintf(text, "Select entrance node (right-click to cancel)");
grcDebugDraw::Text( Vector2(0.05f,fYPos), Color_green, text, false, fScale, fScale);
}
*/
if(m_iMode == Mode_Normal)
{
/*
if(m_iCurrentEntrance != -1 && m_iCurrentEntrance < temp.m_iNumEntrances)
{
CJunctionTemplate::CEntrance & entrance = temp.m_Entrances[m_iCurrentEntrance];
CNodeAddress iEntranceNode = ThePaths.FindNodeClosestToCoors(entrance.m_vNodePosition, PathfindFindNonJunctionNodeCB, NULL, 2.0f);
Assert(!iEntranceNode.IsEmpty());
CPathNode * pEntranceNode = ThePaths.FindNodePointerSafe(iEntranceNode);
int iNumLanesToJunction, iNumLanesFromJunction;
GetNumLanesForEntranceNode(pEntranceNode, iNumLanesToJunction, iNumLanesFromJunction);
sprintf(text, "Current entrance : %i", m_iCurrentEntrance);
grcDebugDraw::Text( Vector2(0.05f,fYPos), Color_green, text, false, fScale, fScale);
fYPos += fYInc;
sprintf(text, "NumLanesToJunction: %i", iNumLanesToJunction);
grcDebugDraw::Text( Vector2(0.05f,fYPos), Color_green, text, false, fScale, fScale);
fYPos += fYInc;
sprintf(text, "NumLanesFromJunction: %i", iNumLanesFromJunction);
grcDebugDraw::Text( Vector2(0.05f,fYPos), Color_green, text, false, fScale, fScale);
fYPos += fYInc;
fYPos += fYInc/2.0f;
sprintf(text, "Main phase : %i", entrance.m_iPhase);
grcDebugDraw::Text( Vector2(0.05f,fYPos), Color_green, text, false, fScale, fScale);
fYPos += fYInc;
sprintf(text, "Filter-left phase : %i", entrance.m_iLeftFilterLanePhase);
grcDebugDraw::Text( Vector2(0.05f,fYPos), Color_green, text, false, fScale, fScale);
fYPos += fYInc;
sprintf(text, "Filter-right always : %s", entrance.m_bCanTurnRightOnRedLight ? "true" : "false");
grcDebugDraw::Text( Vector2(0.05f,fYPos), Color_green, text, false, fScale, fScale);
fYPos += fYInc;
sprintf(text, "Left land ahead only : %s", entrance.m_bLeftLaneIsAheadOnly ? "true" : "false");
grcDebugDraw::Text( Vector2(0.05f,fYPos), Color_green, text, false, fScale, fScale);
fYPos += fYInc;
}
else
{
sprintf(text, "Current entrance : none");
grcDebugDraw::Text( Vector2(0.05f,fYPos), Color_green, text, false, fScale, fScale);
fYPos += fYInc;
}
*/
}
}
}
#endif // __JUNCTION_EDITOR