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

1838 lines
59 KiB
C++

/////////////////////////////////////////////////////////////////////////////////
//
// FILE : trafficlights.cpp
// PURPOSE : Everything having to do with identifying the lights and making them
// work.
// AUTHOR : Obbe.
// CREATED : 04/05/00
//
/////////////////////////////////////////////////////////////////////////////////
// Framework Headers
#include "grcore/debugdraw.h"
#include "fwmaths/angle.h"
#include "fwmaths/vector.h"
#include "fwdebug/picker.h"
// Game headers
#include "camera/CamInterface.h"
#include "camera/debug/DebugDirector.h"
#include "camera/helpers/Frame.h"
#include "control/gameLogic.h"
#include "vehicleAi/pathFind.h"
#include "control/replay/replay.h"
#include "control/trafficLights.h"
#include "vehicleAi/driverpersonality.h"
#include "game/cheat.h"
#include "game/clock.h"
#include "game/modelIndices.h"
#include "game/weather.h"
#include "game/zones.h"
#include "modelinfo/BaseModelInfoExtensions.h"
#include "objects/Door.h"
#include "Renderer/lights/AsyncLightOcclusionMgr.h"
#include "scene/2deffect.h"
#include "scene/building.h"
#include "scene/RefMacros.h"
#include "scene/world/gameWorld.h"
#include "system/timer.h"
#include "TimeCycle\TimeCycleConfig.h"
#include "Vfx\Misc\BrightLights.h"
#include "Vfx\VfxHelper.h"
#include "physics/WorldProbe/worldprobe.h"
#include "renderer/lights/lights.h"
#include "vehicleAi/vehicleintelligence.h"
#include "vehicleAi/junctions.h"
AI_OPTIMISATIONS()
AI_VEHICLE_OPTIMISATIONS()
float CTrafficLights::TrafficLightsBrightness;
#if __BANK
bool CTrafficLights::ms_bRenderTrafficLightsData = false;
bool CTrafficLights::ms_bRenderTrafficLightsDataForSelectedLightOnly = false;
bool CTrafficLights::ms_bRenderTrafficLightsDataActual = false;
bool CTrafficLights::ms_bAlwaysSearchForJunction = false;
bool CTrafficLights::ms_bAlwaysSearchForEntrance = false;
Color32 CTrafficLights::ms_PedWalkColor;
Color32 CTrafficLights::ms_PedDontWalkColor;
#endif
FW_INSTANTIATE_CLASS_POOL(TrafficLightInfos, CONFIGURED_FROM_FILE, atHashString("TrafficLightInfos",0x80e83c46));
AUTOID_IMPL(TrafficLightInfos);
// Light Settings
static ConfigLightSettings g_TrafficLightNearLightSettings;
static Vec3V g_RedColor;
static Vec3V g_GreenColor;
static Vec3V g_AmberColor;
static Vec3V g_PedWalkColor;
static Vec3V g_PedDontWalkColor;
static float g_farFadeStart;
static float g_farFadeEnd;
static float g_nearFadeStart;
static float g_nearFadeEnd;
/////////////////////////////////////////////////////////////////////////////////
// FUNCTION : SetupModelInfo
// PURPOSE : Helper Method for light damage
/////////////////////////////////////////////////////////////////////////////////
__forceinline u8 TrafficLightCommandToLightComponent( ETrafficLightCommand command )
{
switch(command)
{
case TRAFFICLIGHT_COMMAND_STOP:
return LIGHT_RED;
case TRAFFICLIGHT_COMMAND_AMBERLIGHT:
return LIGHT_AMBER;
case TRAFFICLIGHT_COMMAND_GO:
default:
return LIGHT_GREEN;
}
}
void CTrafficLights::SetConfiguration()
{
if (g_visualSettings.GetIsLoaded())
{
g_TrafficLightNearLightSettings.Set( g_visualSettings, "trafficLight.near", "na" );
g_RedColor = g_visualSettings.GetColor( "trafficLight.red.color" );
g_GreenColor = g_visualSettings.GetColor( "trafficLight.green.color" );
g_AmberColor = g_visualSettings.GetColor( "trafficLight.amber.color" );
g_farFadeStart = g_visualSettings.Get("trafficLight.farFadeStart", 100.0f);
g_farFadeEnd = g_visualSettings.Get("trafficLight.farFadeEnd", 120.0f);
g_nearFadeStart = g_visualSettings.Get("trafficLight.nearFadeStart",30.0f);
g_nearFadeEnd = g_visualSettings.Get("trafficLight.nearFadeEnd",35.0f);
g_PedWalkColor = g_visualSettings.GetColor( "trafficLight.walk.color" );
g_PedDontWalkColor = g_visualSettings.GetColor( "trafficLight.dontwalk.color" );
#if __BANK
ms_PedWalkColor.Set((int)(g_PedWalkColor.GetXf() * 255.0f), (int)(g_PedWalkColor.GetYf() * 255.0f), (int)(g_PedWalkColor.GetZf() * 255.0f));
ms_PedDontWalkColor.Set((int)(g_PedDontWalkColor.GetXf() * 255.0f), (int)(g_PedDontWalkColor.GetYf() * 255.0f), (int)(g_PedDontWalkColor.GetZf() * 255.0f));
#endif
}
}
void CTrafficLights::Init()
{
if( TrafficLightInfos::GetPool() == NULL )
TrafficLightInfos::InitPool( MEMBUCKET_RENDER );
}
/////////////////////////////////////////////////////////////////////////////////
// FUNCTION : InitWidgets
// PURPOSE :
/////////////////////////////////////////////////////////////////////////////////
#if __BANK
void CTrafficLights::InitWidgets()
{
bkBank *pBank = BANKMGR.FindBank("Vehicle AI and Nodes");
Assert(pBank);
pBank->PushGroup("Traffic lights", false);
pBank->AddToggle("Render traffic light/Junction data",&ms_bRenderTrafficLightsData);
pBank->AddToggle("Render traffic light/Junction data for selected light", &ms_bRenderTrafficLightsDataForSelectedLightOnly);
pBank->AddToggle("Always search for junction",&ms_bAlwaysSearchForJunction);
pBank->AddToggle("Alwats search for entrance",&ms_bAlwaysSearchForEntrance);
pBank->AddColor("Ped Boxes: Walk Color",&ms_PedWalkColor);
pBank->AddColor("Ped Boxes: Don't Walk Color",&ms_PedDontWalkColor);
pBank->PopGroup();
}
#endif // __BANK
/////////////////////////////////////////////////////////////////////////////////
// FUNCTION : Update
// PURPOSE : Renders some debug lines for the traffic light objects that we are editing.
/////////////////////////////////////////////////////////////////////////////////
void CTrafficLights::Update()
{
// ---------------------------------------------------------------------
// Calculate the brightness of the traffic light shadows on the ground
//@@: range CTRAFFICLIGHTS_UPDATE {
if (CClock::GetHour() > 20)
{
TrafficLightsBrightness = 1.0f;
}
else if (CClock::GetHour() > 19)
{
//@@: location CTRAFFICLIGHTS_UPDATE_SEVENPM
TrafficLightsBrightness = CClock::GetMinute() / 60.0f;
}
else if (CClock::GetHour() > 6)
{
TrafficLightsBrightness = 0.0f;
}
else if (CClock::GetHour() > 5)
{
//@@: location CTRAFFICLIGHTS_UPDATE_FIVEAM
TrafficLightsBrightness = 1.0f - CClock::GetMinute() / 60.0f;
}
else
{
TrafficLightsBrightness = 1.0f;
}
//@@: } CTRAFFICLIGHTS_UPDATE
TrafficLightsBrightness = rage::Max(TrafficLightsBrightness, g_weather.GetWetness());
TrafficLightsBrightness = rage::Max(TrafficLightsBrightness, g_weather.GetFog());
TrafficLightsBrightness = rage::Max(TrafficLightsBrightness, g_weather.GetRain());
}
/////////////////////////////////////////////////////////////////////////////////
// FUNCTION : ShouldCarStopForLight
// PURPOSE : This function takes the link that the car is at and works out whether
// there is a light that the car should stop for.
/////////////////////////////////////////////////////////////////////////////////
s32 CTrafficLights::CalculateNodeLightCycle(const CPathNode * pTrafficLightNode)
{
Assert(pTrafficLightNode->NumLinks());
Vector3 vTrafficNodeCoors, vVec;
pTrafficLightNode->GetCoors(vTrafficNodeCoors);
Assertf(pTrafficLightNode->IsTrafficLight(), "Path node at %f,%f,%f is not a traffic light, but code was expecting one",vTrafficNodeCoors.x,vTrafficNodeCoors.y,vTrafficNodeCoors.z);
for(s32 l=0; l<pTrafficLightNode->NumLinks(); l++)
{
const CPathNodeLink & link = ThePaths.GetNodesLink(pTrafficLightNode, l);
if(link.IsShortCut())
continue;
const CPathNode * pNextNode = ThePaths.FindNodePointerSafe(link.m_OtherNode);
if(pNextNode && pNextNode->IsJunctionNode())
{
bool bUseOriginalDir = true;
if (link.m_1.m_bDontUseForNavigation && !pTrafficLightNode->m_2.m_slipJunction)
{
#if __DEV
bool bFoundGoodNewNewLink = false;
#endif //__DEV
CPathNodeLink PreEntranceLink;
for (int i = 0; i < pTrafficLightNode->NumLinks(); i++)
{
PreEntranceLink = ThePaths.GetNodesLink(pTrafficLightNode, i);
if (PreEntranceLink.m_OtherNode != pNextNode->m_address
&& !PreEntranceLink.IsShortCut())
{
#if __DEV
bFoundGoodNewNewLink = true;
#endif //__DEV
break;
}
}
#if __DEV
Assert(bFoundGoodNewNewLink);
#endif //__DEV
const CPathNode* pPreEntranceNode = ThePaths.FindNodePointerSafe(PreEntranceLink.m_OtherNode);
if (pPreEntranceNode)
{
bUseOriginalDir = false;
vVec = pTrafficLightNode->GetPos() - pPreEntranceNode->GetPos();
vVec.z = 0.0f;
vVec.NormalizeFast();
}
}
if (bUseOriginalDir)
{
pNextNode->GetCoors(vVec);
vVec = vVec - vTrafficNodeCoors;
vVec.z = 0.0f;
vVec.NormalizeFast();
}
if(Abs(vVec.x) > Abs(vVec.y))
{
return TRAFFIC_LIGHT_CYCLE1;
}
else
{
return TRAFFIC_LIGHT_CYCLE2;
}
}
}
return TRAFFIC_LIGHT_CYCLE1;
}
int HelperFindOtherValidLinkIndex(const CPathNode* pNode, const int iExceptionLink)
{
int iRetVal = -1;
for (int i=0; i < pNode->NumLinks(); i++)
{
if (i == iExceptionLink)
{
continue;
}
const CPathNodeLink* pLink = &ThePaths.GetNodesLink(pNode, i);
if (pLink->IsShortCut())
{
continue;
}
iRetVal = i;
break;
}
return iRetVal;
}
bool CTrafficLights::ShouldCarStopForLightNode(const CVehicle* pCar, const CNodeAddress& NodeAddr, eTrafficLightColour * pOutLightCol, const bool bAllowGoIfPastLine)
{
const CPathNode * pNode = ThePaths.FindNodePointerSafe(NodeAddr);
if(pNode && pNode->IsTrafficLight())
{
//early out if this traffic light node is an exit node for the current junction
//we don't want to obey lights that aren't facing us
CNodeAddress EntryNode, ExitNode;
s32 iEntryLane, iExitLane;
CJunction * pJunction = pCar->GetIntelligence()->GetJunction();
if (pJunction && pJunction->FindEntryAndExitNodes(pCar, EntryNode, ExitNode, iEntryLane, iExitLane))
{
if (ExitNode == pNode->m_address)
{
return false;
}
}
//if this light isn't part of our current junction, ignore it
if (pJunction && !pJunction->ContainsEntranceNode(pNode->m_address))
{
return false;
}
const s32 TrafficLightsCycle = CalculateNodeLightCycle(pNode);
if (TrafficLightsCycle != TRAFFIC_LIGHT_NONE)
{
bool bCarShouldStop = false;
bool bJunctionHasPedPhase = false;
s32 cycleOffset = 0;
float cycleScale = 1.0f;
if(pJunction)
{
bJunctionHasPedPhase = pJunction->GetHasPedCrossingPhase();
cycleOffset = pJunction->GetAutoJunctionCycleOffset();
cycleScale = pJunction->GetAutoJunctionCycleScale();
}
if (TrafficLightsCycle == TRAFFIC_LIGHT_CYCLE1)
{
const s32 Light = LightForCars1(cycleOffset, cycleScale, bJunctionHasPedPhase);
u32 turnDir = BIT_NOT_SET;
if (Light == LIGHT_AMBER) //only do this if we might use the results
{
turnDir = pCar->GetIntelligence()->GetJunctionTurnDirection();
}
if (Light == LIGHT_RED || (Light == LIGHT_AMBER && !CDriverPersonality::RunsAmberLights(pCar->GetDriver(), pCar, turnDir)))
{
bCarShouldStop = true;
}
if(pOutLightCol)
*pOutLightCol = (eTrafficLightColour)Light;
}
else if (TrafficLightsCycle == TRAFFIC_LIGHT_CYCLE2)
{
const s32 Light = LightForCars2(cycleOffset, cycleScale, bJunctionHasPedPhase);
u32 turnDir = BIT_NOT_SET;
if (Light == LIGHT_AMBER) //only do this if we might use the results
{
turnDir = pCar->GetIntelligence()->GetJunctionTurnDirection();
}
if (Light == LIGHT_RED || (Light == LIGHT_AMBER && !CDriverPersonality::RunsAmberLights(pCar->GetDriver(), pCar, turnDir)))
{
bCarShouldStop = true;
}
if(pOutLightCol)
*pOutLightCol = (eTrafficLightColour)Light;
}
else if (TrafficLightsCycle == TRAFFIC_LIGHT_STOPSIGN)
{
// TODO: put new stop-sign/give-way code here
}
if ( bCarShouldStop )
{
// We are dealing with a red or amber light.
// Now we have to check whether the car is in the right area. (Just before the node)
//unless the caller has told us they don't care
if (!bAllowGoIfPastLine)
{
return true;
}
//const s32 nextNode = Clamp(node+1, 0, CVehicleNodeList::CAR_MAX_NUM_PATHNODES_STORED-1);
const CPathNode * pNodeNew = ThePaths.FindNodePointerSafe(pCar->GetIntelligence()->GetJunctionNode());
if(pNodeNew)
{
//carNodeCoorsNew and Old might change to point to the link ahead of the junction,
//but trafficLightCoors will always represent the position of the light
Vector3 carNodeCoorsNew, carNodeCoorsOld, trafficLightCoors;
pNodeNew->GetCoors(carNodeCoorsNew);
pNode->GetCoors(carNodeCoorsOld);
pNode->GetCoors(trafficLightCoors);
s16 iEntranceLinkIndex = -1;
if (ThePaths.FindNodesLinkIndexBetween2Nodes(pNode, pNodeNew, iEntranceLinkIndex))
{
const CPathNodeLink* pEntranceLink = &ThePaths.GetNodesLink(pNode, iEntranceLinkIndex);
Assert(pEntranceLink);
if (pEntranceLink->m_1.m_bDontUseForNavigation)
{
#if __ASSERT
const u32 nNumValidLinks = pNode->FindNumberNonShortcutLinks();
Assertf(nNumValidLinks == 2, "Junction entrance has too many links! Region: %d Index: %d Coords: %.2f %.2f %.2f", NodeAddr.GetRegion(), NodeAddr.GetIndex(), pNode->GetPos().x, pNode->GetPos().y, pNode->GetPos().z);
#endif //__ASSERT
//don't use the direction of this link. rather use the dir of the link behind us
const Vector3 potentialCarNodeCoorsNew = carNodeCoorsOld;
const int iPreEntranceLink = HelperFindOtherValidLinkIndex(pNode, iEntranceLinkIndex);
const CPathNodeLink* pPreEntranceLink = &ThePaths.GetNodesLink(pNode, iPreEntranceLink);
Assert(pPreEntranceLink);
const CPathNode* pPreEntranceNode = ThePaths.FindNodePointerSafe(pPreEntranceLink->m_OtherNode);
if (pPreEntranceNode)
{
pPreEntranceNode->GetCoors(carNodeCoorsOld);
carNodeCoorsNew = potentialCarNodeCoorsNew;
}
}
}
// Make sure we aren't already past the node.
Vector3 diff = carNodeCoorsNew - carNodeCoorsOld;
diff.z = 0.0f;
diff.Normalize();
const float fBoundingBoxY = pCar->GetVehicleModelInfo()->GetBoundingBoxMax().y;
const Vector3 vVehPosition = VEC3V_TO_VECTOR3(pCar->GetVehiclePosition()) + (VEC3V_TO_VECTOR3(pCar->GetVehicleForwardDirection()) * fBoundingBoxY);
Vector3 vLightToCar = vVehPosition - trafficLightCoors;
vLightToCar.z = 0.0f;
const float DotPr = DotProduct( vLightToCar, diff );
//we can be up to 1 car length over the line
const float fVehHalfLengthMultiplier = pCar->GetVehicleModelInfo()->GetVehicleFlag(CVehicleModelInfoFlags::FLAG_BIG) ? 1.0f : 2.0f;
if (DotPr < fBoundingBoxY * fVehHalfLengthMultiplier /*&& DotPr > -14.0f*/)
{
return true;
}
}
}
else
{
return false; // If we've checked one set of lights don't check another. Cars can get stuck with 2 consecutive sets of lights.
}
}
}
return false;
}
u32 FindTrafficLightTime(s32 timeOffset, float timeScale, bool pedPhase)
{
u32 Time = NetworkInterface::GetSyncedTimeInMilliseconds() + timeOffset;
if (timeScale != 1.0f)
{
Time = (int)((float)Time * timeScale);
}
return Time % (pedPhase ? LIGHTDURATION_CYCLETIME_PED : LIGHTDURATION_CYCLETIME);
}
/// Cycle for the lights (Red, Amber, Green):
// Time: 0 5000 6000 11000 12000 15384 // THIS IS WRONG.... WORK IT OUT
// Cars1 G A R R R R
// Cars2 R R G A R R
// Peds R R R R G A
/////////////////////////////////////////////////////////////////////////////////
// FUNCTION : LightForCars1
// PURPOSE : Returns the state of the lights for cars (1).
// 0 = Green, 1 = Amber, 2 = Red, 3 = off
/////////////////////////////////////////////////////////////////////////////////
u32 CTrafficLights::LightForCars1(s32 timeOffset, float timeScale, bool pedPhase)
{
u32 Time = FindTrafficLightTime(timeOffset, timeScale, pedPhase);
if (Time < LIGHTDURATION_LONGERGREEN) return (LIGHT_GREEN);
if (Time < LIGHTDURATION_LONGERGREEN+LIGHTDURATION_AMBER) return (LIGHT_AMBER);
return (LIGHT_RED);
}
/////////////////////////////////////////////////////////////////////////////////
// FUNCTION : LightForCars2
// PURPOSE : Returns the state of the lights for cars (2).
// 0 = Green, 1 = Amber, 2 = Red, 3 = off
/////////////////////////////////////////////////////////////////////////////////
u32 CTrafficLights::LightForCars2(s32 timeOffset, float timeScale, bool pedPhase)
{
u32 Time = FindTrafficLightTime(timeOffset, timeScale, pedPhase);
if (Time < LIGHTDURATION_LONGERGREEN+LIGHTDURATION_AMBER) return (LIGHT_RED);
if (Time < LIGHTDURATION_LONGERGREEN+LIGHTDURATION_AMBER+LIGHTDURATION_SHORTERGREEN) return (LIGHT_GREEN);
if(pedPhase)
{
if (Time < LIGHTDURATION_LONGERGREEN+LIGHTDURATION_AMBER+LIGHTDURATION_AMBER+LIGHTDURATION_SHORTERGREEN) return (LIGHT_AMBER);
return (LIGHT_RED);
}
return (LIGHT_AMBER);
}
/////////////////////////////////////////////////////////////////////////////////
// FUNCTION : LightForPeds
// PURPOSE : Returns the state of the lights for peds.
// 0 = Green, 1 = Amber, 2 = Red
/////////////////////////////////////////////////////////////////////////////////
u32 CTrafficLights::LightForPeds(s32 timeOffset, float timeScale, float dirX, float dirY, bool pedPhase, float safeTimeRatio)
{
u32 Time = FindTrafficLightTime(timeOffset, timeScale, pedPhase);
// If the junction has a ped phase
if( pedPhase )
{
// The time must be past both direction green light times
if( Time > LIGHTDURATION_LONGERGREEN+LIGHTDURATION_AMBER+LIGHTDURATION_SHORTERGREEN+LIGHTDURATION_AMBER )
{
// The time must be before the safety time remaining to the end
// Note that safeTimeRatio may be zero.
if( Time < (LIGHTDURATION_CYCLETIME_PED - safeTimeRatio*(LIGHTDURATION_PEDS + LIGHTDURATION_AMBER)) )
{
// traffic stopped for peds, return red light so peds cross
return LIGHT_RED;
}
}
// some traffic is moving, return green light so peds wait
return LIGHT_GREEN;
}
// otherwise, the junction has no ped phase:
// Compute whether this is crossing lights 1 by direction
if( rage::Abs(dirY) > rage::Abs(dirX) )
{
// The time must be past lights 1 green
if( Time > LIGHTDURATION_LONGERGREEN+LIGHTDURATION_AMBER )
{
// The time must be before the safety time remaining to the end
// Note that safeTimeRatio may be zero
if( Time < (LIGHTDURATION_CYCLETIME - safeTimeRatio*(LIGHTDURATION_SHORTERGREEN+LIGHTDURATION_AMBER)) )
{
// return red light so peds cross
return LIGHT_RED;
}
}
}
else // crossing lights 2
{
// The time must be before the safety time remaining to the end
// Note that safeTimeRation may be zero
if( Time < LIGHTDURATION_LONGERGREEN+LIGHTDURATION_AMBER - safeTimeRatio*(LIGHTDURATION_LONGERGREEN+LIGHTDURATION_AMBER))
{
// return red light so peds cross
return LIGHT_RED;
}
}
// return green light so peds wait
return LIGHT_GREEN;
}
//static dev_float radius = 0.12f;
static dev_float aOffset = 0.0f;
static dev_float bOffset = -0.1f;
static dev_float redOffset = TRAFFIC_LIGHT_CORONA_VERTICLE_OFFSET;
static dev_float orangeOffset = 0.0f;
static dev_float greenOffset = -TRAFFIC_LIGHT_CORONA_VERTICLE_OFFSET;
void CTrafficLights::RenderLight(ETrafficLightCommand command, const Mat34V &matrix, const CEntity *BANK_ONLY(trafficLight), float farFade, float nearFade, float brightlightAlpha, bool BANK_ONLY(turnright) )
{
const ScalarV aOffsetV = ScalarVFromF32(aOffset);
const ScalarV bOffsetV = ScalarVFromF32(bOffset);
const ScalarV redOffsetV = ScalarVFromF32(redOffset);
const ScalarV orangeOffsetV = ScalarVFromF32(orangeOffset);
const ScalarV greenOffsetV = ScalarVFromF32(greenOffset);
BANK_ONLY(Color32 col;)
Vec3V lightCol;
Vec3V lightPos;
BrightLightType_e lightType;
if( command == TRAFFICLIGHT_COMMAND_STOP )
{
BANK_ONLY(col = Color32(0x7f,0x00,0x00,0x7f);)
lightPos = matrix.GetCol0()*aOffsetV + matrix.GetCol1()*bOffsetV + matrix.GetCol2()*redOffsetV + matrix.GetCol3();
lightType = BRIGHTLIGHTTYPE_VEH_TRAFFIC_RED;
lightCol = g_RedColor;
}
else if ( command == TRAFFICLIGHT_COMMAND_AMBERLIGHT )
{
BANK_ONLY(col = Color32(0x7f,0x40,0x00,0x7f);)
lightPos = matrix.GetCol0()*aOffsetV + matrix.GetCol1()*bOffsetV + matrix.GetCol2()*orangeOffsetV + matrix.GetCol3();
lightType = BRIGHTLIGHTTYPE_VEH_TRAFFIC_AMBER;
lightCol = g_AmberColor;
}
else
{
BANK_ONLY(col = Color32(0x00,0x7f,0x00,0x7f);)
lightPos = matrix.GetCol0()*aOffsetV + matrix.GetCol1()*bOffsetV + matrix.GetCol2()*greenOffsetV + matrix.GetCol3();
lightType = BRIGHTLIGHTTYPE_VEH_TRAFFIC_GREEN;
lightCol = g_GreenColor;
}
g_brightLights.Register(lightType,
lightPos,
matrix.GetCol2(),
matrix.GetCol0(),
matrix.GetCol1(),
RCC_VECTOR3(lightCol),
g_TrafficLightNearLightSettings.coronaSize,
g_TrafficLightNearLightSettings.coronaHDR * farFade,
true,
brightlightAlpha);
#if __BANK && DEBUG_DRAW
if( ms_bRenderTrafficLightsDataActual )
{
Vec3V bonePos = matrix.GetCol3();
if( turnright ) col.SetAlpha(255);
grcDebugDraw::Sphere(RCC_VECTOR3(bonePos) + VEC3V_TO_VECTOR3(trafficLight->GetTransform().GetC()) * 1.0f,0.5f,col);
}
#endif // __DEV
float nearLightIntensity = TrafficLightsBrightness * g_TrafficLightNearLightSettings.intensity * nearFade;
if( nearLightIntensity > 0.02f )
{
// here we are going to create a spot light that is pushed away from the traffic corona, and then pointed
// to shine back on it. We are also going to rotate the spot light 5 degrees towards the ground because
// it looks better that way - It just makes it easier to see from the ground and helps it pop a bit.
Matrix34 lightMatrix = MAT34V_TO_MATRIX34( matrix );
lightMatrix.RotateLocalX( -5 * (PI / 180.0f) );
lightPos -= matrix.GetCol1();
Vector3 worldDir, worldTan;
worldDir = lightMatrix.GetVector(1);
worldTan = lightMatrix.GetVector(0);
const float radius = g_TrafficLightNearLightSettings.radius;
const float falloffExp = g_TrafficLightNearLightSettings.falloffExp;
const float innerConeAngle = g_TrafficLightNearLightSettings.innerConeAngle;
const float outerConeAngle = g_TrafficLightNearLightSettings.outerConeAngle;
CLightSource* pLightSource = CAsyncLightOcclusionMgr::AddLight();
if (pLightSource)
{
pLightSource->Reset();
pLightSource->SetCommon( LIGHT_TYPE_SPOT,
LIGHTFLAG_NOT_IN_REFLECTION | LIGHTFLAG_DONT_LIGHT_ALPHA,
RCC_VECTOR3(lightPos),
RCC_VECTOR3(lightCol),
nearLightIntensity,
LIGHT_ALWAYS_ON);
pLightSource->SetDirTangent(worldDir, worldTan);
pLightSource->SetRadius(radius);
pLightSource->SetFalloffExponent(falloffExp);
pLightSource->SetSpotlight(innerConeAngle, outerConeAngle);
}
// Don't call AddSceneLight - CAsyncLightOcclusionMgr will handle adding this light if it passes the
// occlusion test
/* this code below is commented out for performance reasons -jkinz */
/******
// now we are going to create another spot light. This one is going to be pointed down towards
// the center of the intersection (in the direction the traffic light is facing). With this one
// we are hoping to get a bit of light play bouncing off cars as they pass through the intersection
lightPos += matrix.GetCol1();
CLightSource carLight( LIGHT_TYPE_SPOT,
LIGHTFLAG_NOT_IN_REFLECTION | LIGHTFLAG_DONT_LIGHT_ALPHA,
RCC_VECTOR3(lightPos),
RCC_VECTOR3(lightCol),
nearLightIntensity,
LIGHT_ALWAYS_ON);
lightMatrix.RotateLocalX( -115 * (PI / 180.0f) );
worldDir = lightMatrix.GetVector(1);
worldTan = lightMatrix.GetVector(0);
carLight.SetDirTangent(worldDir, worldTan);
carLight.SetRadius(9.0f);
carLight.SetFalloffExponent(24);
carLight.SetSpotlight(5.0f, 55.0f);
carLight.SetSpecularFadeDistance(10);
Lights::AddSceneLight(carLight);
*******/
}
}
void CTrafficLights::RenderPedLight(ETrafficLightCommand command, const Mat34V &matrix)
{
BrightLightType_e lightType;
Vector3 lightColor;
if( command == TRAFFICLIGHT_COMMAND_PED_WALK )
{
lightType = BRIGHTLIGHTTYPE_PED_TRAFFIC_GREEN;
lightColor = VEC3V_TO_VECTOR3(g_PedWalkColor);
BANK_ONLY(lightColor.Set(ms_PedWalkColor.GetRedf(), ms_PedWalkColor.GetGreenf(), ms_PedWalkColor.GetBluef());)
}
else //if ( command == TRAFFICLIGHT_COMMAND_PED_DONTWALK )
{
lightType = BRIGHTLIGHTTYPE_PED_TRAFFIC_RED;
lightColor = VEC3V_TO_VECTOR3(g_PedDontWalkColor);
BANK_ONLY(lightColor.Set(ms_PedDontWalkColor.GetRedf(), ms_PedDontWalkColor.GetGreenf(), ms_PedDontWalkColor.GetBluef());)
}
g_brightLights.Register(lightType,
matrix.GetCol3(),
matrix.GetCol2(),
-matrix.GetCol1(),
matrix.GetCol0(),
lightColor,
1.25f, //default value
1.0f, //default value
false,
1.0); //alpha = 1.0
}
ETrafficLightCommand CTrafficLights::GetTrafficLightCommand(const CPathNode * pathnode, s32 timeOffset, float timeScale, bool pedPhase)
{
// Probably an old school light.
ETrafficLightCommand command = TRAFFICLIGHT_COMMAND_AMBERLIGHT;
if( !pathnode->IsGiveWay() )
{
int lightType = CalculateNodeLightCycle(pathnode);
int lightState = LIGHT_GREEN;
if( lightType == TRAFFIC_LIGHT_CYCLE1 )
{
lightState = LightForCars1(timeOffset, timeScale, pedPhase);
}
else if( lightType == TRAFFIC_LIGHT_CYCLE2 )
{
lightState = LightForCars2(timeOffset, timeScale, pedPhase);
}
switch(lightState)
{
case LIGHT_RED:
command = TRAFFICLIGHT_COMMAND_STOP;
break;
case LIGHT_AMBER:
command = TRAFFICLIGHT_COMMAND_AMBERLIGHT;
break;
case LIGHT_GREEN:
command = TRAFFICLIGHT_COMMAND_GO;
break;
}
}
return command;
}
s32 CTrafficLights::GetJunctionIdx(CEntity *entity, const BaseModelInfoBoneIndices* pExtension)
{
const Vector3 vEntityPosition = VEC3V_TO_VECTOR3(entity->GetTransform().GetPosition());
const Vector3 vEntityDirection = VEC3V_TO_VECTOR3(entity->GetTransform().GetB());
const bool isSingleLight = ( pExtension->GetBoneCount() == 1 );
return CJunctions::GetJunctionAtPositionForTrafficLight(vEntityPosition, vEntityDirection, isSingleLight);
}
CJunction *CTrafficLights::GetJunction(s32 idx)
{
if( idx != -1 )
{
return CJunctions::GetJunctionByIndex(idx);
}
return NULL;
}
bool CTrafficLights::SetupTrafficLightInfo(CEntity *entity, TrafficLightInfos *tli, const BaseModelInfoBoneIndices* pExtension, CJunction *junction)
{
const bool isSingleLight = ( pExtension->GetBoneCount() == 1 );
atRangeArray<int,4> entranceIds; // We deal with a maximum of 4 entrances per junction/traffic lights.
Vector3 dir = VEC3V_TO_VECTOR3(entity->GetTransform().GetB());
int entranceCount = junction->FindTrafficLightEntranceIds(dir,isSingleLight,entranceIds);
int renderLaneId = 0;
int entranceIdx = -1;
int curlaneId = 0;
for(int i=0;i<entranceCount;i++)
{
entranceIdx = entranceIds[i];
const CJunctionEntrance &entrance = junction->GetEntrance(entranceIdx);
const CPathNode * pEntranceNode = ThePaths.FindNodePointerSafe(entrance.m_Node);
// We have the entrance node.
// Now we need to find the link to one of the junction nodes.
for(int jn=0; jn<junction->GetNumJunctionNodes(); jn++)
{
const CPathNode * junctionNode = ThePaths.FindNodePointerSafe( junction->GetJunctionNode(jn) );
if(junctionNode)
{
s16 iLinkIndex;
if( ThePaths.FindNodesLinkIndexBetween2Nodes( pEntranceNode->m_address, junctionNode->m_address, iLinkIndex ) )
{
const CPathNodeLink & link = ThePaths.GetNodesLink( pEntranceNode, iLinkIndex );
const int iNumLanes = link.m_1.m_LanesToOtherNode;
for(curlaneId = 0;curlaneId<iNumLanes;curlaneId++,renderLaneId++)
{
const int boneIdx = pExtension->GetBoneIndice((eTrafficLightComponentId)renderLaneId);
if( boneIdx != 0xff )
{
tli->SetLink(renderLaneId,entranceIdx,curlaneId);
}
}
break;
}
}
}
}
if( entranceIdx != -1 )
{
// Duplicate the last lane's info to the remaining lights
for(;renderLaneId<TRAFFIC_LIGHT_COUNT;renderLaneId++)
{
tli->SetLink(renderLaneId,entranceIdx,curlaneId);
}
return true;
}
return false;
}
#if __BANK
#define MAX_TEMP_ENTRANCES 4
static void DebugDrawJunctionNodes(CEntity *entity, CJunction *junction)
{
const float dotPMax = -0.9f;
int entranceCount = 0;
int entranceIds[4] = { -1,-1,-1,-1 }; // We deal with a maximum of 4 entrances per junction/traffic lights.
if( junction->HasTrafficLightNodes() )
{
grcDebugDraw::Sphere(RCC_VEC3V(junction->GetJunctionCenter()), 1.0f, Color_gold);
}
// these vars are used in the case that we can't find an entrance for a light
atRangeArray<int,MAX_TEMP_ENTRANCES> tempEntranceIds;
float lowestDotProduct = 1.0f;
int tempEntranceCount = 0;
for(int i=0;i<junction->GetNumEntrances();i++)
{
const CJunctionEntrance &entrance = junction->GetEntrance(i);
const Vector3 direction(entrance.m_vEntryDir,Vector2::kXY);
float dotP = DotProduct(direction, VEC3V_TO_VECTOR3(entity->GetTransform().GetB()));
if( dotP < dotPMax )
{
entranceIds[entranceCount] = i;
entranceCount++;
}
else if( entranceCount == 0 )
{
// if there haven't been any entrances found yet, start storing up the data
// for the "best fit" entrances if, in the end, we don't find any
if( dotP < lowestDotProduct )
{
// if the dot product is lower, that means this is even more of an "opposite" entrance
// than what we had stored beforehand, so lets whipe out all the old entrances and start again
tempEntranceCount = 0;
lowestDotProduct = dotP;
memset(&tempEntranceIds[0], 0, sizeof(int) * MAX_TEMP_ENTRANCES);
tempEntranceIds[tempEntranceCount++] = i;
}
else if( IsClose( dotP, lowestDotProduct, FLT_EPSILON ) )
{
tempEntranceIds[tempEntranceCount++] = i;
}
}
}
// if we found no entrances, lets takea look at the closest matches for consideration...
if( entranceCount == 0 && tempEntranceCount != 0 )
{
entranceCount = tempEntranceCount;
for( int i = 0; i < tempEntranceCount; i++ )
{
entranceIds[i] = tempEntranceIds[i];
}
}
// We deal from the inside entrance to the outside one, so first is the filterleft, then the rest
for(int i=0;i<entranceCount;i++)
{
const CJunctionEntrance &entranceI = junction->GetEntrance(entranceIds[i]);
bool isFilterLeftLaneI = (entranceI.m_iLeftFilterPhase != -1);
for(int j=i+1;j<entranceCount;j++)
{
const CJunctionEntrance &entranceJ = junction->GetEntrance(entranceIds[j]);
bool isFilterLeftLaneJ = (entranceJ.m_iLeftFilterPhase != -1);
if( false == isFilterLeftLaneI && true == isFilterLeftLaneJ )
{
int tmp = entranceIds[i];
entranceIds[i] = entranceIds[j];
entranceIds[j] = tmp;
}
}
}
int entranceIdx = -1;
int renderLaneId = 0;
for(int i=0;i<entranceCount;i++)
{
entranceIdx = entranceIds[i];
const CJunctionEntrance &entrance = junction->GetEntrance(entranceIdx);
const CPathNode * pEntranceNode = ThePaths.FindNodePointerSafe(entrance.m_Node);
if( pEntranceNode )
{
Vector3 coords;
pEntranceNode->GetCoors(coords);
if( pEntranceNode->IsTrafficLight() )
{
grcDebugDraw::Sphere(RCC_VEC3V(coords), 0.5f, Color_yellow);
grcDebugDraw::Text(RCC_VEC3V(coords),Color32(0xffffff),"T");
}
else
{
grcDebugDraw::Sphere(RCC_VEC3V(coords), 0.5f, Color_blue);
}
// We have the entrance node.
// Now we need to find the link to one of the junction nodes.
for(int jn=0; jn<junction->GetNumJunctionNodes(); jn++)
{
const CPathNode * junctionNode = ThePaths.FindNodePointerSafe( junction->GetJunctionNode(jn) );
if(junctionNode)
{
junctionNode->GetCoors(coords);
if( junctionNode->IsTrafficLight() )
{
grcDebugDraw::Sphere(RCC_VEC3V(coords), 0.5f, Color_white);
}
else
{
grcDebugDraw::Sphere(RCC_VEC3V(coords), 0.5f, Color_black);
}
s16 iLinkIndex;
if( ThePaths.FindNodesLinkIndexBetween2Nodes( pEntranceNode->m_address, junctionNode->m_address, iLinkIndex ) )
{
const CPathNodeLink & link = ThePaths.GetNodesLink( pEntranceNode, iLinkIndex );
const int iNumLanes = link.m_1.m_LanesToOtherNode;
for(int curlaneId = 0;curlaneId<iNumLanes;curlaneId++,renderLaneId++)
{
// just bump renderLaneId...
}
break;
}
}
}
}
}
}
#endif
void CTrafficLights::RemoveTrafficLightInfo(CEntity *entity)
{
TrafficLightInfos::Remove(entity);
}
void CTrafficLights::TransferTrafficLightInfo(CEntity *src, CEntity *dst)
{
TrafficLightInfos *tli = TrafficLightInfos::Get(src);
if( tli )
{
ASSERT_ONLY(TrafficLightInfos *tliDst = TrafficLightInfos::Get(dst));
Assert(tliDst == NULL);
TrafficLightInfos::Unlink(src);
TrafficLightInfos::Link(dst,tli);
CJunction *junction = GetJunction(tli->GetJunctionIdx());
if( junction )
{
// Link up the traffic light to the junction
junction->SetTrafficLight(tli->GetTrafficLightIdx(),dst);
}
}
}
#if GTA_REPLAY
void CTrafficLights::RenderLightsReplay(CEntity* pEntity, const char* commands)
{
if(!commands)
return;
CBaseModelInfo *pBaseModel = pEntity->GetBaseModelInfo();
const float entityAlpha = ((float)pEntity->GetAlpha())/255.0f;
const float camDist = (VEC3V_TO_VECTOR3(pEntity->GetTransform().GetPosition()) - camInterface::GetPos()).Mag();
const float farFade = rage::Clamp( (g_farFadeEnd - camDist)/(g_farFadeEnd - g_farFadeStart),0.0f,entityAlpha);
const float nearFade = rage::Clamp( (g_nearFadeEnd - camDist)/(g_nearFadeEnd - g_nearFadeStart),0.0f,entityAlpha);
const float brightLightFade = entityAlpha; // simply use entity alpha
const BaseModelInfoBoneIndices* pExtension = CTrafficLights::GetExtension(pBaseModel);
if(pExtension == NULL )
{
return;
}
const int boneCount = pExtension->GetBoneCount();
for(int boneId = 0;boneId<boneCount;boneId++)
{
int boneIdx = pExtension->GetBoneIndice((eTrafficLightComponentId)boneId);
if( boneIdx != 0xff )
{
Mat34V boneMtx;
CVfxHelper::GetMatrixFromBoneIndex_Lights(boneMtx, pEntity, boneIdx);
ETrafficLightCommand lightCommand = (ETrafficLightCommand)commands[boneId];
//don't set invalid commands, it will default to green if rendered
if(lightCommand != TRAFFICLIGHT_COMMAND_INVALID)
{
RenderLight(lightCommand, boneMtx, pEntity,farFade,nearFade,brightLightFade,false);
}
}
}
ETrafficLightCommand pedLightCommand = (ETrafficLightCommand)commands[PED_WALK_BOX];
if (pedLightCommand != TRAFFICLIGHT_COMMAND_INVALID)
{
int boneIdx = pExtension->GetBoneIndice((eTrafficLightComponentId)PED_WALK_BOX);
if( boneIdx != 0xff )
{
Mat34V boneMtx;
CVfxHelper::GetMatrixFromBoneIndex_Lights(boneMtx, pEntity, boneIdx);
RenderPedLight(pedLightCommand, boneMtx);
}
}
}
#endif // GTA_REPLAY
void CTrafficLights::RenderLights(CEntity *pEntity)
{
#if GTA_REPLAY
if(CReplayMgr::IsEditModeActive())
{
if(pEntity->GetIsTypeDummyObject() || pEntity->GetIsTypeBuilding())
return; // Bail on dummy's atm as they won't have a replay id
RenderLightsReplay(pEntity, CReplayMgr::GetTrafficLightCommands(pEntity));
return;
}
#endif // GTA_REPLAY
if (pEntity->m_nFlags.bRenderDamaged) return; // Damaged. Lights no working
CBaseModelInfo *pBaseModel = pEntity->GetBaseModelInfo();
// bail out if the drawable isn't loaded in yet
if (pBaseModel->GetDrawable() == NULL)
return;
const BaseModelInfoBoneIndices* pExtension = CTrafficLights::GetExtension(pBaseModel);
if(pExtension == NULL )
return;
TrafficLightInfos *tli = TrafficLightInfos::Get(pEntity);
if( NULL == tli )
{
tli = TrafficLightInfos::Add(pEntity);
}
if(tli && !tli->AreMatricesCached() && pBaseModel->GetFragType())
{
tli->CacheBoneMatrices(pEntity, pExtension);
}
#if __BANK
ms_bRenderTrafficLightsDataActual = ms_bRenderTrafficLightsData || (ms_bRenderTrafficLightsDataForSelectedLightOnly == true && pEntity == g_PickerManager.GetSelectedEntity());
#endif // __BANK
const float entityAlpha = ((float)pEntity->GetAlpha())/255.0f;
const float camDist = (VEC3V_TO_VECTOR3(pEntity->GetTransform().GetPosition()) - camInterface::GetPos()).Mag();
const float farFade = rage::Clamp( (g_farFadeEnd - camDist)/(g_farFadeEnd - g_farFadeStart),0.0f,entityAlpha);
const float nearFade = rage::Clamp( (g_nearFadeEnd - camDist)/(g_nearFadeEnd - g_nearFadeStart),0.0f,entityAlpha);
const float brightLightFade = entityAlpha; // simply use entity alpha
static const float fAmberTime = ((float)LIGHTDURATION_AMBER) / 1000.0f;
#if __DEV
if( ms_bRenderTrafficLightsDataActual )
{
Mat34V temp;
u32 boneIdx;
boneIdx = pExtension->GetBoneIndice(TRAFFIC_LIGHT_0);
if( boneIdx != 0xff )
{
CVfxHelper::GetMatrixFromBoneIndex_Lights(temp, pEntity, boneIdx);
grcDebugDraw::Axis(RCC_MATRIX34(temp),0.5f,true);
}
boneIdx = pExtension->GetBoneIndice(TRAFFIC_LIGHT_1);
if( boneIdx != 0xff )
{
CVfxHelper::GetMatrixFromBoneIndex_Lights(temp, pEntity, boneIdx);
grcDebugDraw::Axis(RCC_MATRIX34(temp),0.5f,true);
}
boneIdx = pExtension->GetBoneIndice(TRAFFIC_LIGHT_2);
if( boneIdx != 0xff )
{
CVfxHelper::GetMatrixFromBoneIndex_Lights(temp, pEntity, boneIdx);
grcDebugDraw::Axis(RCC_MATRIX34(temp),0.5f,true);
}
boneIdx = pExtension->GetBoneIndice(TRAFFIC_LIGHT_3);
if( boneIdx != 0xff )
{
CVfxHelper::GetMatrixFromBoneIndex_Lights(temp, pEntity, boneIdx);
grcDebugDraw::Axis(RCC_MATRIX34(temp),0.5f,true);
}
boneIdx = pExtension->GetBoneIndice(PED_WALK_BOX);
if( boneIdx != 0xff )
{
CVfxHelper::GetMatrixFromBoneIndex_Lights(temp, pEntity, boneIdx);
grcDebugDraw::Axis(RCC_MATRIX34(temp),0.5f,true);
}
temp = pEntity->GetTransform().GetMatrix();
grcDebugDraw::Axis(RCC_MATRIX34(temp),1.0f,true);
}
#endif // __DEV
u32 tlOverride = pEntity->GetTrafficLightOverride();
if( tlOverride != 0x3)
{
ETrafficLightCommand command = (ETrafficLightCommand)tlOverride;
// Traffic light state as been overriden
const int boneCount = pExtension->GetBoneCount();
if(tli && tli->AreMatricesCached())
{
for(int boneId = 0;boneId<boneCount;boneId++)
{
int boneIdx = pExtension->GetBoneIndice((eTrafficLightComponentId)boneId);
if( boneIdx != 0xff )
{
const Mat34V& boneMtx = tli->GetMatrixFromBoneIndex(boneId);
RenderLight(command, boneMtx, pEntity,farFade,nearFade,brightLightFade,false);
}
}
}
else
{
for(int boneId = 0;boneId<boneCount;boneId++)
{
int boneIdx = pExtension->GetBoneIndice((eTrafficLightComponentId)boneId);
if( boneIdx != 0xff )
{
Mat34V boneMtx;
CVfxHelper::GetMatrixFromBoneIndex_Lights(boneMtx, pEntity, boneIdx);
RenderLight(command, boneMtx, pEntity,farFade,nearFade,brightLightFade,false);
}
}
}
return;
}
if( tli )
{
const float JUNCTION_CONNECT_DISTANCE = 100.0f;
const bool reEvaluateJunction = (tli->IsSetup() && tli->GetSetupDistance() > JUNCTION_CONNECT_DISTANCE && camDist < JUNCTION_CONNECT_DISTANCE);
if( tli->GetJunctionIdx() == -1 || reEvaluateJunction BANK_ONLY(|| (ms_bAlwaysSearchForJunction && pEntity == g_PickerManager.GetSelectedEntity())) )
{
int oldJunction = tli->GetJunctionIdx();
int newJunction = CTrafficLights::GetJunctionIdx(pEntity, pExtension);
// if we found a junction that is not the same as what we previously had...
if( oldJunction != newJunction )
{
CJunction *junction = GetJunction(oldJunction);
// if we were already hooked up to a junction, remove ourselves from that one
if( junction != NULL )
{
junction->RemoveTrafficLight(pEntity);
}
// set the new junction index and then force a re-setup to connect to the junctions entrances properly
tli->Reset();
tli->SetJunctionIdx(newJunction);
tli->SetIsSetup(false);
}
else
{
tli->SetSetupDistance(camDist);
}
}
CJunction *junction = GetJunction(tli->GetJunctionIdx());
if( junction )
{
// Link up the traffic light to the junction
int tlIdx = tli->GetTrafficLightIdx();
if( tlIdx == -1)
{
tlIdx = junction->AddTrafficLight(pEntity);
tli->SetTrafficLightIdx(tlIdx);
}
#if __BANK && DEBUG_DRAW
if( ms_bRenderTrafficLightsDataActual )
{
Vector3 vUp = ZAXIS*0.5f;
grcDebugDraw::Arrow(RCC_VEC3V(junction->GetJunctionCenter()) + RCC_VEC3V(vUp), pEntity->GetTransform().GetPosition()+RCC_VEC3V(vUp), 0.3f, Color32(0xFFFFFFFF));
grcDebugDraw::Sphere(junction->GetJunctionCenter(),junction->GetTrafficLightSearchDistance(),Color32(255,0,0,64),false);
int templateIdx = junction->GetTemplateIndex();
if( templateIdx > -1 )
{
const CJunctionTemplate &junctionTemplate = CJunctions::GetJunctionTemplate(templateIdx);
for(int i=0;i<junctionTemplate.m_iNumTrafficLightLocations;i++)
{
Vector3 pos;
junctionTemplate.m_TrafficLightLocations[i].GetAsVec3(pos);
const Vector3 vEntityPosition = VEC3V_TO_VECTOR3(pEntity->GetTransform().GetPosition());
if( (vEntityPosition - pos).Mag2() <= 6.0f * 6.0f )
{
grcDebugDraw::Sphere(pos,6.0f,Color32(255,128,255,64),false,1,16);
}
else
{
grcDebugDraw::Sphere(pos,6.0f,Color32(64,32,64,64),false,1,16);
}
}
}
}
#endif // __DEV
#if __BANK
if( ms_bRenderTrafficLightsDataActual )
DebugDrawJunctionNodes(pEntity, junction);
if( ms_bAlwaysSearchForEntrance && pEntity == g_PickerManager.GetSelectedEntity() )
tli->SetIsSetup(false);
#endif // __BANK
if( tli->IsSetup() == false && junction->GetNumEntrances() != 0)
{
// Go through the junction and attach the right lane to the right light.
bool result = SetupTrafficLightInfo(pEntity, tli, pExtension, junction);
tli->SetIsSetup(result);
if( result )
{
tli->SetSetupDistance(camDist);
#if USE_TLI_COORDINATECHECK
tli->SetCoord(junction->GetJunctionCenter());
#endif // USE_TLI_COORDINATECHECK
}
}
if( tli->IsSetup() )
{
#if USE_TLI_COORDINATECHECK
Assertf(tli->GetCoord() == junction->GetJunctionCenter(),"MissMatched Junction/TLI: %p %p",tli,junction);
#endif // USE_TLI_COORDINATECHECK
if( junction->GetTemplateIndex() == -1 )
{
// Old school: axis commands the light
REPLAY_ONLY(char commands[8] = {0};)
const int boneCount = pExtension->GetBoneCount();
for(int boneId = 0;boneId<boneCount;boneId++)
{
if (boneId == PED_WALK_BOX)
continue;
int boneIdx = pExtension->GetBoneIndice((eTrafficLightComponentId)boneId);
if( boneIdx != 0xff )
{
const CJunctionEntrance &entrance = junction->GetEntrance(tli->GetEntranceID(boneId));
const CPathNode * pNode = ThePaths.FindNodePointerSafe(entrance.m_Node);
if( pNode )
{
ETrafficLightCommand command = GetTrafficLightCommand(pNode,
junction->GetAutoJunctionCycleOffset(),
junction->GetAutoJunctionCycleScale(),
junction->GetHasPedCrossingPhase());
// if the light is damaged, don't render it
u8 lightId = TrafficLightCommandToLightComponent(command);
if( tli->IsLightDamaged((u8)boneId, lightId) )
continue;
Mat34V boneMtx;
if(tli->AreMatricesCached())
{
const Mat34V& boneMtx = tli->GetMatrixFromBoneIndex(boneId);
RenderLight(command, boneMtx, pEntity,farFade,nearFade,brightLightFade,false);
}
else
{
Mat34V boneMtx;
CVfxHelper::GetMatrixFromBoneIndex_Lights(boneMtx, pEntity, boneIdx);
RenderLight(command, boneMtx, pEntity,farFade,nearFade,brightLightFade,false);
}
REPLAY_ONLY(commands[boneId] = (char)command;)
#if __BANK
if( ms_bRenderTrafficLightsDataActual )
{
Color32 col = Color32(0x00,0x7f,0x00,0x7f);
switch(command)
{
case TRAFFICLIGHT_COMMAND_STOP:
col = Color32(0x7f,0x00,0x00,0x7f);
break;
case TRAFFICLIGHT_COMMAND_AMBERLIGHT:
col = Color32(0x7f,0x40,0x00,0x7f);
break;
default:
break;
}
grcDebugDraw::Line(boneMtx.d(),VECTOR3_TO_VEC3V(pNode->GetPos()),col,Color_gold);
}
#endif // __BANK
}
}
}
REPLAY_ONLY(ETrafficLightCommand trafficLightCommand =) RenderPedLights(pEntity,pExtension,tli,junction);
#if GTA_REPLAY
if (trafficLightCommand != TRAFFICLIGHT_COMMAND_INVALID)
{
commands[PED_WALK_BOX] = (char)trafficLightCommand;
}
if(CReplayMgr::ShouldRecord())
CReplayMgr::SetTrafficLightCommands(pEntity, commands);
#endif // GTA_REPLAY
}
else
{
REPLAY_ONLY(char commands[8] = {0};)
const bool bStopForTrain = junction->ShouldCarsStopForTrain();
const int boneCount = pExtension->GetBoneCount();
for(int boneId = 0;boneId<boneCount;boneId++)
{
if (boneId == PED_WALK_BOX)
continue;
int boneIdx = pExtension->GetBoneIndice((eTrafficLightComponentId)boneId);
if( boneIdx != 0xff )
{
const CJunctionEntrance &entrance = junction->GetEntrance(tli->GetEntranceID(boneId));
ETrafficLightCommand command = TRAFFICLIGHT_COMMAND_STOP;
const float fTimeLeft = junction->GetLightTimeRemaining();
if( bStopForTrain )
{
// remain on stop
}
else if( tli->GetLaneId(boneId) == 0 )
{
if( entrance.m_iLeftFilterPhase == -1 && junction->GetLightPhase() == entrance.m_iPhase )
{
command = (fTimeLeft<fAmberTime) ? TRAFFICLIGHT_COMMAND_AMBERLIGHT:TRAFFICLIGHT_COMMAND_GO;
}
else if ( entrance.m_iLeftFilterPhase != -1 && entrance.m_iLeftFilterPhase == junction->GetLightPhase() )
{
command = (fTimeLeft<fAmberTime) ? TRAFFICLIGHT_COMMAND_AMBERLIGHT:TRAFFICLIGHT_COMMAND_GO;
}
}
else if( junction->GetLightPhase() == entrance.m_iPhase )
{
command = (fTimeLeft<fAmberTime) ? TRAFFICLIGHT_COMMAND_AMBERLIGHT:TRAFFICLIGHT_COMMAND_GO;
}
// if the light is damaged, don't render it
u8 lightId = TrafficLightCommandToLightComponent(command);
if( tli->IsLightDamaged((u8)boneId, lightId) )
continue;
Mat34V boneMtx;
if(tli->AreMatricesCached())
{
boneMtx = tli->GetMatrixFromBoneIndex(boneId);
}
else
{
CVfxHelper::GetMatrixFromBoneIndex_Lights(boneMtx, pEntity, boneIdx);
}
RenderLight(command, boneMtx, pEntity,farFade,nearFade,brightLightFade,entrance.m_bCanTurnRightOnRedLight);
REPLAY_ONLY(commands[boneId] = (char)command;)
#if __BANK
if( ms_bRenderTrafficLightsDataActual )
{
Color32 col = Color32(0x00,0x7f,0x00,0x7f);
switch(command)
{
case TRAFFICLIGHT_COMMAND_STOP:
col = Color32(0x7f,0x00,0x00,0x7f);
break;
case TRAFFICLIGHT_COMMAND_AMBERLIGHT:
col = Color32(0x7f,0x40,0x00,0x7f);
break;
default:
break;
}
const CPathNode * pNode = ThePaths.FindNodePointerSafe(entrance.m_Node);
if( pNode )
grcDebugDraw::Line(boneMtx.d(),VECTOR3_TO_VEC3V(pNode->GetPos()),col,Color_gold);
}
#endif // __BANK
}
}
REPLAY_ONLY(ETrafficLightCommand trafficLightCommand =) RenderPedLights(pEntity,pExtension,tli,junction);
#if GTA_REPLAY
if (trafficLightCommand != TRAFFICLIGHT_COMMAND_INVALID)
{
commands[PED_WALK_BOX] = (char)trafficLightCommand;
}
if(CReplayMgr::ShouldRecord())
CReplayMgr::SetTrafficLightCommands(pEntity, commands);
#endif // GTA_REPLAY
}
}
}
}
}
ETrafficLightCommand CTrafficLights::RenderPedLights(CEntity* pEntity, const BaseModelInfoBoneIndices* pExtension, TrafficLightInfos* tli, CJunction* junction)
{
// pedestrian walk lights
if( !tli->IsLightDamaged(PED_WALK_BOX, 0) )
{
int boneIdx = pExtension->GetBoneIndice(PED_WALK_BOX);
if( boneIdx != 0xff )
{
Mat34V boneMtx;
CVfxHelper::GetMatrixFromBoneIndex_Lights(boneMtx, pEntity, boneIdx);
bool showWalk = false;
if( junction->GetTemplateIndex() != -1 )
{
showWalk = (junction->GetLightStatusForPedCrossing(false) == LIGHT_RED);
}
else
{
// at this point we have a non-template junction:
// Return the global default light status for the given crossing direction
Vec3V crossingDirection = boneMtx.GetCol1();
showWalk = (CTrafficLights::LightForPeds(junction->GetAutoJunctionCycleOffset(), junction->GetAutoJunctionCycleScale(), crossingDirection.GetXf(), crossingDirection.GetYf(), junction->GetHasPedCrossingPhase(), 0.0f) == LIGHT_RED);
}
ETrafficLightCommand trafficLightCommand = showWalk ? TRAFFICLIGHT_COMMAND_PED_WALK : TRAFFICLIGHT_COMMAND_PED_DONTWALK;
RenderPedLight(trafficLightCommand, boneMtx);
return trafficLightCommand;
}
}
return TRAFFICLIGHT_COMMAND_INVALID;
}
/////////////////////////////////////////////////////////////////////////////////
// FUNCTION : SetupModelInfo
// PURPOSE : Setup the boneindices extension on a modelinfo
/////////////////////////////////////////////////////////////////////////////////
void CTrafficLights::SetupModelInfo(CBaseModelInfo *modelInfo)
{
const char *nodeNames[] = { "traffic_light_0",
"traffic_light_1",
"traffic_light_2",
"traffic_light_3",
"ped_walk_box",
NULL };
crSkeletonData *skeletonData = modelInfo->GetFragType()->GetCommonDrawable()->GetSkeletonData();
Assertf(skeletonData,"Traffic light %s has no skeleton",modelInfo->GetModelName());
BaseModelInfoBoneIndices *extension = modelInfo->GetExtension<BaseModelInfoBoneIndices>();
if( NULL == extension )
{
BaseModelInfoBoneIndices *boneIds = rage_new BaseModelInfoBoneIndices(skeletonData, nodeNames);
boneIds->CalculateBoneCount();
modelInfo->GetExtensionList().Add(*boneIds);
}
#if __ASSERT
else
{
extension->VerifyBoneIndices(skeletonData, nodeNames);
}
#endif // __ASSERT
}
/////////////////////////////////////////////////////////////////////////////////
// FUNCTION : CacheBoneMatrices
// PURPOSE : Cache the bone matrices on the TrafficLightInfos
/////////////////////////////////////////////////////////////////////////////////
void TrafficLightInfos::CacheBoneMatrices(CEntity *entity, const BaseModelInfoBoneIndices* pBoneIndices)
{
Assert(!mtxsCached);
const int boneCount = pBoneIndices->GetBoneCount();
boneMtxs.Resize(boneCount);
for(int boneId = 0;boneId<boneCount;boneId++)
{
int boneIdx = pBoneIndices->GetBoneIndice((eTrafficLightComponentId)boneId);
if( boneIdx != 0xff )
{
CVfxHelper::GetMatrixFromBoneIndex_Lights(boneMtxs[boneId], entity, boneIdx);
}
}
mtxsCached = true;
}
/////////////////////////////////////////////////////////////////////////////////
// FUNCTION : CRailwayCrossingLights::RenderLights
// PURPOSE : Render railway crossing lights
/////////////////////////////////////////////////////////////////////////////////
void CRailwayCrossingLights::RenderLights(CEntity *pEntity)
{
if (pEntity->m_nFlags.bRenderDamaged) return; // Damaged. Lights no working
CBaseModelInfo *pBaseModel = pEntity->GetBaseModelInfo();
// bail out if the drawable isn't loaded in yet
if (pBaseModel->GetDrawable() == NULL)
return;
const BaseModelInfoBoneIndices* pExtension = CRailwayCrossingLights::GetExtension(pBaseModel);
if (pExtension == NULL)
return;
const Vector3 vEntityPosition = VEC3V_TO_VECTOR3(pEntity->GetTransform().GetPosition());
const s32 iJunctionIndex = CJunctions::GetJunctionAtPositionForRailwayCrossing(vEntityPosition);
if (iJunctionIndex == -1)
{
return;
}
CJunction* pJunction = CJunctions::GetJunctionByIndex(iJunctionIndex);
if (pJunction == NULL)
{
return;
}
ERailwayCrossingLightState railwayCrossingLightState = pJunction->GetRailwayCrossingLightState();
const float entityAlpha = ((float)pEntity->GetAlpha())/255.0f;
//const float camDist = (VEC3V_TO_VECTOR3(pEntity->GetTransform().GetPosition()) - camInterface::GetPos()).Mag();
//const float farFade = rage::Clamp( (g_farFadeEnd - camDist)/(g_farFadeEnd - g_farFadeStart),0.0f,entityAlpha);
//const float nearFade = rage::Clamp( (g_nearFadeEnd - camDist)/(g_nearFadeEnd - g_nearFadeStart),0.0f,entityAlpha);
const float brightLightFade = entityAlpha; // simply use entity alpha
bool bShouldRenderLight = false;
u32 boneCount = pExtension->GetBoneCount();
for (int boneId = 0; boneId < boneCount; boneId++)
{
int boneIdx = pExtension->GetBoneIndice((eRailwayCrossingLightComponentId)boneId);
if( boneIdx != 0xff )
{
bShouldRenderLight = ((railwayCrossingLightState == RAILWAY_CROSSING_LIGHT_PULSE_LEFT) &&
((boneId == RAILWAY_LIGHT_LEFT_0) || (boneId == RAILWAY_LIGHT_LEFT_1) || (boneId == RAILWAY_LIGHT_LEFT_2))) ||
((railwayCrossingLightState == RAILWAY_CROSSING_LIGHT_PULSE_RIGHT) &&
((boneId == RAILWAY_LIGHT_RIGHT_0) || (boneId == RAILWAY_LIGHT_RIGHT_1) || (boneId == RAILWAY_LIGHT_RIGHT_2)));
if (bShouldRenderLight)
{
Mat34V boneMtx;
CVfxHelper::GetMatrixFromBoneIndex_Lights(boneMtx, pEntity, boneIdx);
RenderLight(boneMtx, brightLightFade);
}
}
}
}
/////////////////////////////////////////////////////////////////////////////////
// FUNCTION : CRailwayCrossingLights::IsRailwayBarrierLight
// PURPOSE : Render a railway crossing light
/////////////////////////////////////////////////////////////////////////////////
void CRailwayCrossingLights::RenderLight(const Mat34V &matrix, float brightlightAlpha)
{
const ScalarV forwardOffset = ScalarVFromF32(0.0f);
Vec3V lightPos = matrix.GetCol1() * forwardOffset + matrix.GetCol3();
Vec3V lightCol = g_RedColor;
BrightLightType_e lightType = BRIGHTLIGHTTYPE_RAILWAY_TRAFFIC_RED;
g_brightLights.Register(lightType,
lightPos,
matrix.GetCol2(),
matrix.GetCol0(),
matrix.GetCol1(),
RCC_VECTOR3(lightCol),
0.0f,
0.0f,
true,
brightlightAlpha);
}
/////////////////////////////////////////////////////////////////////////////////
// FUNCTION : CRailwayCrossingLights::SetupModelInfo
// PURPOSE : Setup the bone indices extension on a modelinfo
/////////////////////////////////////////////////////////////////////////////////
void CRailwayCrossingLights::SetupModelInfo(CBaseModelInfo *modelInfo)
{
const char *nodeNames[] = { "railway_light_left_0",
"railway_light_right_0",
"railway_light_left_1",
"railway_light_right_1",
"railway_light_left_2",
"railway_light_right_2",
NULL };
crSkeletonData *skeletonData = modelInfo->GetFragType()->GetCommonDrawable()->GetSkeletonData();
Assertf(skeletonData,"Railway crossing light %s has no skeleton",modelInfo->GetModelName());
BaseModelInfoBoneIndices *extension = modelInfo->GetExtension<BaseModelInfoBoneIndices>();
if( NULL == extension )
{
BaseModelInfoBoneIndices *boneIds = rage_new BaseModelInfoBoneIndices(skeletonData, nodeNames);
boneIds->CalculateBoneCount();
modelInfo->GetExtensionList().Add(*boneIds);
}
#if __ASSERT
else
{
extension->VerifyBoneIndices(skeletonData, nodeNames);
}
#endif // __ASSERT
}
/////////////////////////////////////////////////////////////////////////////////
// FUNCTION : CRailwayBarrierLights::RenderLights
// PURPOSE : Render railway barrier lights
/////////////////////////////////////////////////////////////////////////////////
void CRailwayBarrierLights::RenderLights(CEntity *pEntity)
{
if (pEntity->m_nFlags.bRenderDamaged) return; // Damaged. Lights no working
CBaseModelInfo *pBaseModel = pEntity->GetBaseModelInfo();
// bail out if the drawable isn't loaded in yet
if (pBaseModel->GetDrawable() == NULL)
return;
const BaseModelInfoBoneIndices* pExtension = CRailwayBarrierLights::GetExtension(pBaseModel);
if (pExtension == NULL)
return;
const Vector3 vEntityPosition = VEC3V_TO_VECTOR3(pEntity->GetTransform().GetPosition());
const s32 iJunctionIndex = CJunctions::GetJunctionAtPositionForRailwayCrossing(vEntityPosition);
if (iJunctionIndex == -1)
{
return;
}
CJunction* pJunction = CJunctions::GetJunctionByIndex(iJunctionIndex);
if (pJunction == NULL)
{
return;
}
ERailwayCrossingLightState railwayCrossingLightState = pJunction->GetRailwayCrossingLightState();
const float entityAlpha = ((float)pEntity->GetAlpha())/255.0f;
//const float camDist = (VEC3V_TO_VECTOR3(pEntity->GetTransform().GetPosition()) - camInterface::GetPos()).Mag();
//const float farFade = rage::Clamp( (g_farFadeEnd - camDist)/(g_farFadeEnd - g_farFadeStart),0.0f,entityAlpha);
//const float nearFade = rage::Clamp( (g_nearFadeEnd - camDist)/(g_nearFadeEnd - g_nearFadeStart),0.0f,entityAlpha);
const float brightLightFade = entityAlpha; // simply use entity alpha
bool bShouldRenderLight = false;
u32 boneCount = pExtension->GetBoneCount();
if (boneCount >= 2)
{
for (int boneId = 0; boneId < boneCount; boneId++)
{
int boneIdx = pExtension->GetBoneIndice((eRailwayBarrierLightComponentId)boneId);
if( boneIdx != 0xff )
{
bShouldRenderLight = ((boneId == 0 && boneCount == 2) && (railwayCrossingLightState == RAILWAY_CROSSING_LIGHT_PULSE_LEFT)) ||
((boneId == 1 && boneCount == 2) && (railwayCrossingLightState == RAILWAY_CROSSING_LIGHT_PULSE_RIGHT)) ||
((boneId == 0 && boneCount >= 3) && (railwayCrossingLightState != RAILWAY_CROSSING_LIGHT_OFF)) ||
((boneId == 1 && boneCount >= 3) && (railwayCrossingLightState == RAILWAY_CROSSING_LIGHT_PULSE_LEFT)) ||
((boneId == 2 && boneCount >= 3) && (railwayCrossingLightState == RAILWAY_CROSSING_LIGHT_PULSE_RIGHT)) ||
((boneId >= 3) && (railwayCrossingLightState != RAILWAY_CROSSING_LIGHT_OFF));
if (bShouldRenderLight)
{
Mat34V boneMtx;
CVfxHelper::GetMatrixFromBoneIndex_Lights(boneMtx, pEntity, boneIdx);
RenderLight(boneMtx, brightLightFade);
}
}
}
}
}
/////////////////////////////////////////////////////////////////////////////////
// FUNCTION : CRailwayBarrierLights::IsRailwayBarrierLight
// PURPOSE : Render one of the barrier lights
/////////////////////////////////////////////////////////////////////////////////
void CRailwayBarrierLights::RenderLight(const Mat34V &matrix, float brightlightAlpha)
{
const ScalarV forwardOffset = ScalarVFromF32(-0.001f);
Vec3V lightPos = matrix.GetCol1() * forwardOffset + matrix.GetCol3();
Vec3V lightCol = g_RedColor;
BrightLightType_e lightType = BRIGHTLIGHTTYPE_RAILWAY_BARRIER_RED;
g_brightLights.Register(lightType,
lightPos,
matrix.GetCol2(),
matrix.GetCol0(),
matrix.GetCol1(),
RCC_VECTOR3(lightCol),
0.0f,
0.0f,
true,
brightlightAlpha);
}
/////////////////////////////////////////////////////////////////////////////////
// FUNCTION : CRailwayBarrierLights::IsRailwayBarrierLight
// PURPOSE : Is this a railway barrier light?
/////////////////////////////////////////////////////////////////////////////////
bool CRailwayBarrierLights::IsRailwayBarrierLight(CEntity *pEntity)
{
if(pEntity && pEntity->GetIsTypeObject())
{
CObject* pObj = static_cast<CDoor*>(pEntity);
if (pObj->IsADoor())
{
CDoor* pDoor = static_cast<CDoor*>(pObj);
if (pDoor->GetDoorType() == CDoor::DOOR_TYPE_RAIL_CROSSING_BARRIER)
{
return true;
}
}
}
return false;
}
/////////////////////////////////////////////////////////////////////////////////
// FUNCTION : CRailwayBarrierLights::SetupModelInfo
// PURPOSE : Setup the bone indices extension on a modelinfo
/////////////////////////////////////////////////////////////////////////////////
void CRailwayBarrierLights::SetupModelInfo(CBaseModelInfo *modelInfo)
{
const char *nodeNames[] = { "railway_light_0",
"railway_light_1",
"railway_light_2",
"railway_light_3",
NULL };
crSkeletonData *skeletonData = modelInfo->GetFragType()->GetCommonDrawable()->GetSkeletonData();
Assertf(skeletonData,"Railway barrier %s has no skeleton",modelInfo->GetModelName());
BaseModelInfoBoneIndices *extension = modelInfo->GetExtension<BaseModelInfoBoneIndices>();
if( NULL == extension )
{
BaseModelInfoBoneIndices *boneIds = rage_new BaseModelInfoBoneIndices(skeletonData, nodeNames);
boneIds->CalculateBoneCount();
modelInfo->GetExtensionList().Add(*boneIds);
}
#if __ASSERT
else
{
extension->VerifyBoneIndices(skeletonData, nodeNames);
}
#endif // __ASSERT
}