CARLA
Loading...
Searching...
No Matches
TrafficLightManager.cpp
Go to the documentation of this file.
1// Copyright (c) 2026 Computer Vision Center (CVC) at the Universitat Autonoma
2// de Barcelona (UAB).
3//
4// This work is licensed under the terms of the MIT license.
5// For a copy, see <https://opensource.org/licenses/MIT>.
6
8#include "GeoTrafficSign.h"
9#include "Game/CarlaStatics.h"
11#include "StopSignComponent.h"
12#include "YieldSignComponent.h"
13#include "SpeedLimitComponent.h"
15#include "Components/BoxComponent.h"
16#include "Engine/Level.h"
17#include "Engine/World.h"
18#include "HAL/IConsoleManager.h"
19#include "TrafficLightBase.h"
20#include "Runtime/CoreUObject/Public/UObject/ConstructorHelpers.h"
21#include "OpenDrive/OpenDrive.h"
23
24#include "UObject/ConstructorHelpers.h"
25
27#include <carla/rpc/String.h>
31
32
33#include <string>
34
35static TAutoConsoleVariable<bool> CVarCarlaHideSpawnedSignsWhenPlaced(
36 TEXT("carla.TrafficSigns.HideSpawnedWhenPlaced"),
37 true,
38 TEXT("On World Partition maps, hide the signs spawned from the OpenDRIVE once a sign ")
39 TEXT("placed in the level streams in. Their traffic logic stays. Read at level load."),
40 ECVF_Default);
41
43{
44 PrimaryActorTick.bCanEverTick = true;
45 PrimaryActorTick.bStartWithTickEnabled = false;
46 SceneComponent = CreateDefaultSubobject<USceneComponent>(TEXT("RootComponent"));
47 RootComponent = SceneComponent;
48
49 // Hard coded default traffic light blueprint
50 static ConstructorHelpers::FClassFinder<AActor> TrafficLightRHTFinder(
51 TEXT( "/Game/Carla/Blueprints/TrafficLight/BP_TLOpenDrive_RHT" ) );
52 if (TrafficLightRHTFinder.Succeeded())
53 {
54 TSubclassOf<AActor> Model = TrafficLightRHTFinder.Class;
56 }
57
58 // Hard coded default traffic light blueprint
59 static ConstructorHelpers::FClassFinder<AActor> TrafficLightLHTFinder(
60 TEXT( "/Game/Carla/Blueprints/TrafficLight/BP_TLOpenDrive_LHT" ) );
61 if (TrafficLightLHTFinder.Succeeded())
62 {
63 TSubclassOf<AActor> Model = TrafficLightLHTFinder.Class;
65 }
66 // Default traffic signs models
67 static ConstructorHelpers::FClassFinder<AActor> StopFinder(
68 TEXT( "/Game/Carla/Static/TrafficSign/BP_Stop01" ) );
69 if (StopFinder.Succeeded())
70 {
71 TSubclassOf<AActor> StopSignModel = StopFinder.Class;
72 TrafficSignsModels.Add(carla::road::SignalType::StopSign().c_str(), StopSignModel);
73 SignComponentModels.Add(carla::road::SignalType::StopSign().c_str(), UStopSignComponent::StaticClass());
74 }
75 static ConstructorHelpers::FClassFinder<AActor> YieldFinder(
76 TEXT( "/Game/Carla/Static/TrafficSign/BP_Yield01" ) );
77 if (YieldFinder.Succeeded())
78 {
79 TSubclassOf<AActor> YieldSignModel = YieldFinder.Class;
80 TrafficSignsModels.Add(carla::road::SignalType::YieldSign().c_str(), YieldSignModel);
81 SignComponentModels.Add(carla::road::SignalType::YieldSign().c_str(), UYieldSignComponent::StaticClass());
82 }
83 static ConstructorHelpers::FClassFinder<AActor> SpeedLimit30Finder(
84 TEXT( "/Game/Carla/Static/TrafficSign/BP_SpeedLimit30" ) );
85 if (SpeedLimit30Finder.Succeeded())
86 {
87 TSubclassOf<AActor> SpeedLimitModel = SpeedLimit30Finder.Class;
88 SpeedLimitModels.Add("30", SpeedLimitModel);
89 }
90 static ConstructorHelpers::FClassFinder<AActor> SpeedLimit40Finder(
91 TEXT( "/Game/Carla/Static/TrafficSign/BP_SpeedLimit40" ) );
92 if (SpeedLimit40Finder.Succeeded())
93 {
94 TSubclassOf<AActor> SpeedLimitModel = SpeedLimit40Finder.Class;
95 SpeedLimitModels.Add("40", SpeedLimitModel);
96 }
97 static ConstructorHelpers::FClassFinder<AActor> SpeedLimit50Finder(
98 TEXT( "/Game/Carla/Static/TrafficSign/BP_SpeedLimit50" ) );
99 if (SpeedLimit50Finder.Succeeded())
100 {
101 TSubclassOf<AActor> SpeedLimitModel = SpeedLimit50Finder.Class;
102 SpeedLimitModels.Add("50", SpeedLimitModel);
103 }
104 static ConstructorHelpers::FClassFinder<AActor> SpeedLimit60Finder(
105 TEXT( "/Game/Carla/Static/TrafficSign/BP_SpeedLimit60" ) );
106 if (SpeedLimit60Finder.Succeeded())
107 {
108 TSubclassOf<AActor> SpeedLimitModel = SpeedLimit60Finder.Class;
109 SpeedLimitModels.Add("60", SpeedLimitModel);
110 }
111 static ConstructorHelpers::FClassFinder<AActor> SpeedLimit70Finder(
112 TEXT( "/Game/Carla/Static/TrafficSign/BP_SpeedLimit70" ) );
113 if (SpeedLimit70Finder.Succeeded())
114 {
115 TSubclassOf<AActor> SpeedLimitModel = SpeedLimit70Finder.Class;
116 SpeedLimitModels.Add("70", SpeedLimitModel);
117 }
118 static ConstructorHelpers::FClassFinder<AActor> SpeedLimit80Finder(
119 TEXT( "/Game/Carla/Static/TrafficSign/BP_SpeedLimit80" ) );
120 if (SpeedLimit80Finder.Succeeded())
121 {
122 TSubclassOf<AActor> SpeedLimitModel = SpeedLimit80Finder.Class;
123 SpeedLimitModels.Add("80", SpeedLimitModel);
124 }
125 static ConstructorHelpers::FClassFinder<AActor> SpeedLimit90Finder(
126 TEXT( "/Game/Carla/Static/TrafficSign/BP_SpeedLimit90" ) );
127 if (SpeedLimit90Finder.Succeeded())
128 {
129 TSubclassOf<AActor> SpeedLimitModel = SpeedLimit90Finder.Class;
130 SpeedLimitModels.Add("90", SpeedLimitModel);
131 }
132 static ConstructorHelpers::FClassFinder<AActor> SpeedLimit100Finder(
133 TEXT( "/Game/Carla/Static/TrafficSign/BP_SpeedLimit100" ) );
134 if (SpeedLimit100Finder.Succeeded())
135 {
136 TSubclassOf<AActor> SpeedLimitModel = SpeedLimit100Finder.Class;
137 SpeedLimitModels.Add("100", SpeedLimitModel);
138 }
139 static ConstructorHelpers::FClassFinder<AActor> SpeedLimit110Finder(
140 TEXT( "/Game/Carla/Static/TrafficSign/BP_SpeedLimit110" ) );
141 if (SpeedLimit110Finder.Succeeded())
142 {
143 TSubclassOf<AActor> SpeedLimitModel = SpeedLimit110Finder.Class;
144 SpeedLimitModels.Add("110", SpeedLimitModel);
145 }
146 static ConstructorHelpers::FClassFinder<AActor> SpeedLimit120Finder(
147 TEXT( "/Game/Carla/Static/TrafficSign/BP_SpeedLimit120" ) );
148 if (SpeedLimit120Finder.Succeeded())
149 {
150 TSubclassOf<AActor> SpeedLimitModel = SpeedLimit120Finder.Class;
151 SpeedLimitModels.Add("120", SpeedLimitModel);
152 }
153 // US (MUTCD) mph plates for maps whose signals carry country="US". The assets are the
154 // legacy US-style blueprints (mph value on a rectangular plate).
155 struct FUSPlate { const TCHAR *Key; const TCHAR *Path; };
156 const FUSPlate USPlates[] = {
157 {TEXT("20"), TEXT("/Game/Carla/Static/TrafficSign/BP_SpeedLimit20_15")},
158 {TEXT("25"), TEXT("/Game/Carla/Static/TrafficSign/BP_SpeedLimit25_15")},
159 {TEXT("30"), TEXT("/Game/Carla/Static/TrafficSign/BP_SpeedLimit30_15")},
160 {TEXT("50"), TEXT("/Game/Carla/Static/TrafficSign/BP_SpeedLimit50_40")},
161 {TEXT("55"), TEXT("/Game/Carla/Static/TrafficSign/BP_SpeedLimit55_40")},
162 {TEXT("75"), TEXT("/Game/Carla/Static/TrafficSign/BP_SpeedLimit75_45")},
163 };
164 for (const FUSPlate &Plate : USPlates)
165 {
166 ConstructorHelpers::FClassFinder<AActor> Finder(Plate.Path);
167 if (Finder.Succeeded())
168 {
169 SpeedLimitModels_US.Add(Plate.Key, Finder.Class);
170 }
171 }
172
174}
175
176void ATrafficLightManager::RegisterLightComponentFromOpenDRIVE(UTrafficLightComponent * TrafficLightComponent)
177{
179 check(GM);
180
181 // Cast to std::string
182 carla::road::SignId SignId(TCHAR_TO_UTF8(*(TrafficLightComponent->GetSignId())));
183
184 ATrafficLightGroup* TrafficLightGroup;
185 UTrafficLightController* TrafficLightController;
186
187 const auto &Signal = GetMap()->GetSignals().at(SignId);
188 if(Signal->GetControllers().size())
189 {
190 // Only one controller per signal
191 auto ControllerId = *(Signal->GetControllers().begin());
192
193 // Get controller
194 const auto &Controller = GetMap()->GetControllers().at(ControllerId);
195 if(Controller->GetJunctions().empty())
196 {
197 UE_LOG(LogCarla, Error, TEXT("No junctions in controller %d"), *(ControllerId.c_str()) );
198 return;
199 }
200 // Get junction of the controller
201 auto JunctionId = *(Controller->GetJunctions().begin());
202
203 // Search/create TrafficGroup (junction traffic light manager)
204 if(!TrafficGroups.Contains(JunctionId))
205 {
206 FActorSpawnParameters SpawnParams;
207 SpawnParams.OverrideLevel = GM->GetULevelFromName("TrafficLights");
208 auto * NewTrafficLightGroup =
209 GetWorld()->SpawnActor<ATrafficLightGroup>(SpawnParams);
210 NewTrafficLightGroup->JunctionId = JunctionId;
211 TrafficGroups.Add(JunctionId, NewTrafficLightGroup);
212 }
213 TrafficLightGroup = TrafficGroups[JunctionId];
214
215 // Search/create controller in the junction
216 if(!TrafficControllers.Contains(ControllerId.c_str()))
217 {
218 auto *NewTrafficLightController = NewObject<UTrafficLightController>();
219 NewTrafficLightController->SetControllerId(ControllerId.c_str());
220 TrafficLightGroup->AddController(NewTrafficLightController);
221 TrafficControllers.Add(ControllerId.c_str(), NewTrafficLightController);
222 }
223 TrafficLightController = TrafficControllers[ControllerId.c_str()];
224 }
225 else
226 {
227 FActorSpawnParameters SpawnParams;
228 SpawnParams.OverrideLevel = GM->GetULevelFromName("TrafficLights");
229 auto * NewTrafficLightGroup =
230 GetWorld()->SpawnActor<ATrafficLightGroup>(SpawnParams);
231 NewTrafficLightGroup->JunctionId = TrafficLightGroupMissingId;
232 TrafficGroups.Add(NewTrafficLightGroup->JunctionId, NewTrafficLightGroup);
233 TrafficLightGroup = NewTrafficLightGroup;
234
235 auto *NewTrafficLightController = NewObject<UTrafficLightController>();
236 NewTrafficLightController->SetControllerId(FString::FromInt(TrafficLightControllerMissingId));
237 NewTrafficLightController->SetRedTime(10);
238 TrafficLightGroup->AddController(NewTrafficLightController);
239 TrafficControllers.Add(NewTrafficLightController->GetControllerId(), NewTrafficLightController);
240 TrafficLightController = NewTrafficLightController;
241
244 }
245
246 TrafficLightController->AddTrafficLight(TrafficLightComponent);
247 TrafficLightController->ResetState();
248 TrafficSignComponents.Add(TrafficLightComponent->GetSignId(), TrafficLightComponent);
249
250 TrafficLightGroup->ResetGroup();
251
252}
253
255{
256 auto* Controller = TrafficLight->GetController();
257 auto* Group = TrafficLight->GetGroup();
258 if (!Controller || !Group)
259 {
260 UE_LOG(LogCarla, Error, TEXT("Missing group or controller for traffic light"));
261 return;
262 }
263 if (TrafficLight->GetSignId() == "")
264 {
265 TrafficLight->SetSignId(FString::FromInt(TrafficLightComponentMissingId));
267 }
268 if (Controller->GetControllerId() == "")
269 {
270 Controller->SetControllerId(FString::FromInt(TrafficLightControllerMissingId));
272 }
273 if (Group->GetJunctionId() == -1)
274 {
275 Group->JunctionId = TrafficLightGroupMissingId;
277 }
278
279 if (!TrafficControllers.Contains(Controller->GetControllerId()))
280 {
281 TrafficControllers.Add(Controller->GetControllerId(), Controller);
282 }
283 if (!TrafficGroups.Contains(Group->GetJunctionId()))
284 {
285 TrafficGroups.Add(Group->GetJunctionId(), Group);
286 }
287 if (!TrafficSignComponents.Contains(TrafficLight->GetSignId()))
288 {
290 }
291}
292
293const std::optional<carla::road::Map>& ATrafficLightManager::GetMap()
294{
295 return UCarlaStatics::GetGameMode(GetWorld())->GetMap();
296}
297
299{
301 {
303 {
304 UE_LOG(LogCarla, Error, TEXT("Missing TrafficLightModel"));
305 return;
306 }
307
309
311
312 SpawnSignals();
313
315 {
317 }
318
320 }
321}
322
324{
325 TArray<AActor*> IgnoredActors;
326 IgnoredActors.Reserve(TrafficSigns.Num());
327 for (ATrafficSignBase* Sign : TrafficSigns)
328 {
329 if (IsValid(Sign))
330 {
331 IgnoredActors.Add(Sign);
332 }
333 }
334 return IgnoredActors;
335}
336
338{
339 UWorld* World = GetWorld();
340 const TArray<AActor*> IgnoredActors = GetSignsToIgnoreWhileTracing();
341 const TArray<UPrimitiveComponent*> NoIgnoredComponents;
342 bool bAnyAdjusted = false;
343 int32 GroundNotFoundCount = 0;
344 for (ATrafficSignBase* Sign : TrafficSigns)
345 {
347 World, Sign, IgnoredActors, NoIgnoredComponents))
348 {
349 bAnyAdjusted = true;
350 }
351 else if (IsValid(Sign) && !Sign->bPositioned && Sign->bGeneratedFromOpenDRIVE)
352 {
353 // Hand-placed signs are skipped by design, so they are not failures.
354 ++GroundNotFoundCount;
355 }
356 }
357 if (GroundNotFoundCount > 0)
358 {
359 UE_LOG(LogCarla, Warning,
360 TEXT("Could not find ground for %d traffic sign(s)"),
361 GroundNotFoundCount);
362 }
363 return bAnyAdjusted;
364}
365
367{
369
370 // Signs are generated at map load, before a client can apply world settings,
371 // so honour a late enable by re-snapping the already-spawned signs.
373}
374
376{
377 // AdjustSpawnedSignsHeight is idempotent (the bPositioned flag guards each
378 // sign), so calling this again is safe.
381 {
382 if (ACarlaGameModeBase* GameMode = UCarlaStatics::GetGameMode(GetWorld()))
383 {
384 GameMode->RegisterEnvironmentObjects();
385 }
386 }
387}
388
390{
391 for(auto& Sign : TrafficSigns)
392 {
393 Sign->Destroy();
394 }
395 TrafficSigns.Empty();
396 SpawnedSigns.Empty();
398
399 for(auto& TrafficGroup : TrafficGroups)
400 {
401 TrafficGroup.Value->Destroy();
402 }
403 TrafficGroups.Empty();
404
405 TrafficControllers.Empty();
406
408}
409
411{
412 TArray<AActor*> Actors;
413 UGameplayStatics::GetAllActorsOfClass(GetWorld(), ATrafficLightBase::StaticClass(), Actors);
414
415 std::string opendrive_xml = carla::rpc::FromFString(UOpenDrive::GetXODR(GetWorld()));
416 auto Map = carla::opendrive::OpenDriveParser::Load(opendrive_xml);
417
418 if (!Map)
419 {
420 carla::log_warning("Map not found");
421 return;
422 }
423
424 TArray<ATrafficLightBase*> TrafficLights;
425 for (AActor* Actor : Actors)
426 {
427 ATrafficLightBase* TrafficLight = Cast<ATrafficLightBase>(Actor);
428 if (TrafficLight)
429 {
430 TrafficLight->GetTrafficLightComponent()->SetSignId("");
431 TrafficLights.Add(TrafficLight);
432 }
433 }
434
435 if (!TrafficLights.Num())
436 {
437 carla::log_warning("No actors in the map");
438 return;
439 }
440
441 const auto& Signals = Map->GetSignals();
442 const auto& Controllers = Map->GetControllers();
443
444 for(const auto& Signal : Signals) {
445 const auto& ODSignal = Signal.second;
446 const FTransform Transform = ODSignal->GetTransform();
447 const FVector Location = Transform.GetLocation();
448 if (ODSignal->GetName() == "")
449 {
450 continue;
451 }
452 ATrafficLightBase* ClosestActor = TrafficLights.Top();
453 float MinDistance = FVector::DistSquaredXY(TrafficLights.Top()->GetActorLocation(), Location);
454 for (ATrafficLightBase* Actor : TrafficLights)
455 {
456 float Distance = FVector::DistSquaredXY(Actor->GetActorLocation(), Location);
457 if (Distance < MinDistance)
458 {
459 MinDistance = Distance;
460 ClosestActor = Actor;
461 }
462 }
463 ATrafficLightBase* TrafficLight = ClosestActor;
464 auto* Component = TrafficLight->GetTrafficLightComponent();
465 if (Component->GetSignId() == "")
466 {
467 Component->SetSignId(carla::rpc::ToFString(ODSignal->GetSignalId()));
468 }
469 else
470 {
471 carla::log_warning("Could not find a suitable traffic light for signal", ODSignal->GetSignalId(),
472 "Closest traffic light has id", carla::rpc::FromFString(Component->GetSignId()));
473 }
474
475 }
476}
477
479{
480 if (!GetMap())
481 {
482 carla::log_warning("Coud not generate traffic lights: missing map.");
483 return;
484 }
485
486 FString MapName = GetWorld()->GetMapName();
487 FString XODRPath = UOpenDrive::FindPathToXODRFile(MapName);
488 bool bHasMapLogic = false;
489
490 if (!XODRPath.IsEmpty())
491 {
492 FString DirectoryPath;
493 FString Filename, Extension;
494 FPaths::Split(XODRPath, DirectoryPath, Filename, Extension);
495 FString JsonFilePath = FPaths::Combine(DirectoryPath, TEXT("map_logic.json"));
496 bHasMapLogic = FPlatformFileManager::Get().GetPlatformFile().FileExists(*JsonFilePath);
497 }
498
500 {
501 if (bHasMapLogic)
502 {
504 SpawnSignals();
506 }
507 else
508 {
510 }
511 }
512
513 if (!XODRPath.IsEmpty() && bHasMapLogic)
514 {
516 }
517
518 if (GetWorld()->GetWorldPartition() != nullptr)
519 {
520 SetActorTickEnabled(true);
521 if (!LevelAddedHandle.IsValid())
522 {
523 LevelAddedHandle = FWorldDelegates::LevelAddedToWorld.AddUObject(
525 }
526 for (ULevel *Level : GetWorld()->GetLevels())
527 {
529 }
530 }
531}
532
533void ATrafficLightManager::EndPlay(const EEndPlayReason::Type EndPlayReason)
534{
535 FWorldDelegates::LevelAddedToWorld.Remove(LevelAddedHandle);
536 LevelAddedHandle.Reset();
537 Super::EndPlay(EndPlayReason);
538}
539
540void ATrafficLightManager::OnLevelAddedToWorld(ULevel *Level, UWorld *World)
541{
542 if (World == GetWorld())
543 {
545 }
546}
547
549{
550 if (bSpawnedSignsSuppressed || Level == nullptr || SpawnedSigns.Num() == 0 ||
551 !CVarCarlaHideSpawnedSignsWhenPlaced.GetValueOnGameThread())
552 {
553 return;
554 }
555 AActor *PlacedSign = nullptr;
556 for (AActor *Actor : Level->Actors)
557 {
558 // Traffic lights are sign actors too, and are left out: this is about
559 // the plates.
560 if (IsValid(Actor) && Actor->IsA<ATrafficSignBase>() && !Actor->IsA<ATrafficLightBase>() &&
561 Actor->GetOwner() != this && !SpawnedSigns.Contains(Cast<ATrafficSignBase>(Actor)))
562 {
563 PlacedSign = Actor;
564 break;
565 }
566 }
567 if (PlacedSign == nullptr)
568 {
569 return;
570 }
572 int32 Hidden = 0;
573 for (const TWeakObjectPtr<ATrafficSignBase> &Weak : SpawnedSigns)
574 {
575 ATrafficSignBase *Sign = Weak.Get();
576 if (!IsValid(Sign))
577 {
578 continue;
579 }
580 Sign->SetActorHiddenInGame(true);
581 TArray<UPrimitiveComponent*> Primitives;
582 Sign->GetComponents(Primitives);
583 for (UPrimitiveComponent *Primitive : Primitives)
584 {
585 // The trigger boxes carry the stop/yield/speed logic.
586 if (!Primitive->IsA<UBoxComponent>())
587 {
588 Primitive->SetCollisionEnabled(ECollisionEnabled::NoCollision);
589 }
590 }
591 ++Hidden;
592 }
593 UE_LOG(LogCarla, Log,
594 TEXT("Traffic signs: %s is placed in the level, hiding the %d signs spawned from the OpenDRIVE"),
595 *PlacedSign->GetName(), Hidden);
596}
597
599{
600 TrafficLightModel_RHT = Other.TrafficLightModel_RHT;
601 TrafficLightModel_LHT = Other.TrafficLightModel_LHT;
602 TrafficSignsModels = Other.TrafficSignsModels;
603 SignComponentModels = Other.SignComponentModels;
604 SpeedLimitModels = Other.SpeedLimitModels;
605 bAdjustSignsHeightToGround = Other.bAdjustSignsHeightToGround;
606}
607
608void ATrafficLightManager::Tick(float DeltaSeconds)
609{
610 Super::Tick(DeltaSeconds);
612}
613
615{
616 const int32 Num = TrafficSigns.Num();
617 if (Num == 0)
618 {
619 return;
620 }
621 UWorld *World = GetWorld();
622 // A World Partition map streams its ground in around the streaming source,
623 // so a signal generated over an unloaded cell finds nothing to stand on and
624 // keeps the height the OpenDRIVE record gave it. This sweep is already the
625 // place that learns when the ground below a signal becomes resident, so the
626 // snap rides it instead of running once at generation time.
627 const bool bSnapToGround = bAdjustSignsHeightToGround;
628 const TArray<AActor*> IgnoredActors =
629 bSnapToGround ? GetSignsToIgnoreWhileTracing() : TArray<AActor*>();
630 const TArray<UPrimitiveComponent*> NoIgnoredComponents;
631 bool bSweepWrapped = false;
632 const int32 Checks = FMath::Min(DormancyChecksPerTick, Num);
633 for (int32 i = 0; i < Checks; ++i)
634 {
636 bSweepWrapped |= (DormancySweepIndex == 0);
638 if (!IsValid(Sign))
639 {
640 continue;
641 }
642 // Hidden for good: the map's placed signs stand in for it.
643 if (bSpawnedSignsSuppressed && SpawnedSigns.Contains(Sign))
644 {
645 continue;
646 }
647 const FVector Origin = Sign->GetActorLocation();
648 FCollisionQueryParams Params(SCENE_QUERY_STAT(SignalGroundDormancy), false, Sign);
649 FHitResult Hit;
650 const bool bGroundResident = World->LineTraceSingleByChannel(
651 Hit,
652 Origin + FVector(0.0f, 0.0f, 50.0f),
653 Origin - FVector(0.0f, 0.0f, DormancyTraceDepth),
654 ECC_Visibility,
655 Params);
656 if (Sign->IsHidden() == bGroundResident)
657 {
658 Sign->SetActorHiddenInGame(!bGroundResident);
659 }
660 if (bSnapToGround && bGroundResident &&
662 World, Sign, IgnoredActors, NoIgnoredComponents))
663 {
665 }
666 }
667
668 // Re-registering walks every actor in the world, so it waits for the sweep
669 // to come back round rather than running on every tick that moved a sign.
670 if (bPendingEnvironmentObjectRefresh && bSweepWrapped)
671 {
673 if (ACarlaGameModeBase* GameMode = UCarlaStatics::GetGameMode(World))
674 {
675 GameMode->RegisterEnvironmentObjects();
676 }
677 }
678}
679
680bool MatchSignalAndActor(const carla::road::Signal &Signal, ATrafficSignBase* ClosestTrafficSign)
681{
682 namespace cr = carla::road;
683 if (ClosestTrafficSign)
684 {
685 if ((Signal.GetType() == cr::SignalType::StopSign()) &&
686 ClosestTrafficSign->GetTrafficSignState() == ETrafficSignState::StopSign)
687 {
688 return true;
689 }
690 else if ((Signal.GetType() == cr::SignalType::YieldSign()) &&
691 ClosestTrafficSign->GetTrafficSignState() == ETrafficSignState::YieldSign)
692 {
693 return true;
694 }
695 else if (cr::SignalType::IsTrafficLight(Signal.GetType()))
696 {
697 if (ClosestTrafficSign->GetTrafficSignState() == ETrafficSignState::TrafficLightRed ||
700 return true;
701 }
702 else if (Signal.GetType() == cr::SignalType::MaximumSpeed())
703 {
704 // The subtype is the limit in km/h; the actor answers in km/h for every speed-limit
705 // state (dedicated or custom), so any value the map carries can be matched.
706 const int32 SignalKmh = FCString::Atoi(*FString(Signal.GetSubtype().c_str()));
707 return SignalKmh > 0 && ClosestTrafficSign->GetSpeedLimitKmh() == SignalKmh;
708 }
709 }
710 return false;
711}
712
713template<typename T = ATrafficSignBase>
714T * GetClosestTrafficSignActor(const carla::road::Signal &Signal, UWorld* World)
715{
716 auto CarlaTransform = Signal.GetTransform();
717 // The signal transform carries the OpenDRIVE zOffset (mounting height of
718 // the plate above the road); baked sign actors have their pivot at road
719 // level, so match against the road-level position.
720 CarlaTransform.location.z -= static_cast<float>(Signal.GetZOffset());
721 FTransform UETransform(CarlaTransform);
722 FVector Location = UETransform.GetLocation();
723 // max distance to match 500cm
724 constexpr float MaxDistanceMatchSqr = 250000.0;
725 T * ClosestTrafficSign = nullptr;
726 TArray<AActor*> Actors;
727 UGameplayStatics::GetAllActorsOfClass(World, T::StaticClass(), Actors);
728 float MinDistance = MaxDistanceMatchSqr;
729 for (AActor* Actor : Actors)
730 {
731 float Dist = FVector::DistSquared(Actor->GetActorLocation(), Location);
732 T * TrafficSign = Cast<T>(Actor);
733 // Baked twin signs carry an explicit identity. Their support may be moved
734 // onto a sidewalk more than 5 m from the logical OpenDRIVE control point.
735 // Never adopt another signal's identified sign through the proximity fallback.
736 if (const AGeoTrafficSign* GeoSign = Cast<AGeoTrafficSign>(Actor))
737 {
738 if (!GeoSign->SignalId.IsEmpty())
739 {
740 if (GeoSign->SignalId == carla::rpc::ToFString(Signal.GetSignalId()) &&
741 MatchSignalAndActor(Signal, TrafficSign))
742 {
743 return TrafficSign;
744 }
745 continue;
746 }
747 }
748 if (Dist < MinDistance && MatchSignalAndActor(Signal, TrafficSign))
749 {
750 ClosestTrafficSign = TrafficSign;
751 MinDistance = Dist;
752 }
753 }
754 return ClosestTrafficSign;
755}
756
758{
759 namespace cr = carla::road;
760 const auto& Signals = GetMap()->GetSignals();
761 std::unordered_set<std::string> SignalsToSpawn;
762 for(const auto& ControllerPair : GetMap()->GetControllers())
763 {
764 const auto& Controller = ControllerPair.second;
765 for(const auto& SignalId : Controller->GetSignals())
766 {
767 auto& Signal = Signals.at(SignalId);
768 if (!cr::SignalType::IsTrafficLight(Signal->GetType()))
769 {
770 continue;
771 }
772 ATrafficLightBase * TrafficLight = GetClosestTrafficSignActor<ATrafficLightBase>(*Signal, GetWorld());
773 if (TrafficLight)
774 {
775 UTrafficLightComponent *TrafficLightComponent = TrafficLight->GetTrafficLightComponent();
776 TrafficLightComponent->SetSignId(SignalId.c_str());
777 }
778 else
779 {
780 SignalsToSpawn.insert(SignalId);
781 }
782 }
783 }
784
785 for(const auto& SignalPair : Signals)
786 {
787 const auto& SignalId = SignalPair.first;
788 const auto& Signal = SignalPair.second;
789 if(!Signal->GetControllers().size() &&
790 !GetMap()->IsJunction(Signal->GetRoadId()) &&
791 carla::road::SignalType::IsTrafficLight(Signal->GetType()) &&
792 !SignalsToSpawn.count(SignalId))
793 {
794 ATrafficLightBase * TrafficLight = GetClosestTrafficSignActor<ATrafficLightBase>(*Signal, GetWorld());
795 if (TrafficLight)
796 {
797 UTrafficLightComponent *TrafficLightComponent = TrafficLight->GetTrafficLightComponent();
798 TrafficLightComponent->SetSignId(SignalId.c_str());
799 }
800 else
801 {
802 SignalsToSpawn.insert(SignalId);
803 }
804 }
805 }
806
808 check(GM);
809 for(auto &SignalId : SignalsToSpawn)
810 {
811 // TODO: should this be an assert?
812 // RELEASE_ASSERT(
813 // Signals.count(SignalId) > 0,
814 // "Reference to inexistent signal. Possible OpenDRIVE error.");
815 if (Signals.count(SignalId) == 0)
816 {
817 UE_LOG(LogCarla, Warning,
818 TEXT("Possible OpenDRIVE error, reference to nonexistent signal id: %s"),
819 *carla::rpc::ToFString(SignalId));
820 continue;
821 }
822 const auto& Signal = Signals.at(SignalId);
823 auto CarlaTransform = Signal->GetTransform();
824 // The signal transform carries the OpenDRIVE zOffset (mounting height of
825 // the reference point above the road). The spawned blueprint models the
826 // whole signal with its pivot at the pole base, so spawn at road level.
827 CarlaTransform.location.z -= static_cast<float>(Signal->GetZOffset());
828 auto ClosestWaypointToSignal =
829 GetMap()->GetClosestWaypointOnRoad(CarlaTransform.location);
830
831 const bool IsRHT = GetMap()->GetLane(ClosestWaypointToSignal.value()).GetRoad()->IsRHT();
832
833 FTransform SpawnTransform(CarlaTransform);
834
835 FVector SpawnLocation = SpawnTransform.GetLocation();
836 FRotator SpawnRotation(SpawnTransform.GetRotation());
837 // Blueprints are all rotated by 90 degrees
838 SpawnRotation.Yaw += 90;
839 // Remove road inclination
840 SpawnRotation.Roll = 0;
841 SpawnRotation.Pitch = 0;
842
843 FActorSpawnParameters SpawnParams;
844 SpawnParams.Owner = this;
845 SpawnParams.SpawnCollisionHandlingOverride =
846 ESpawnActorCollisionHandlingMethod::AlwaysSpawn;
847 SpawnParams.OverrideLevel = GM->GetULevelFromName("TrafficLights");
848
849 auto TrafficLightModel = IsRHT ? TrafficLightModel_RHT : TrafficLightModel_LHT;
850 ATrafficLightBase * TrafficLight = GetWorld()->SpawnActor<ATrafficLightBase>(
851 TrafficLightModel,
852 SpawnLocation,
853 SpawnRotation,
854 SpawnParams);
855
856 // Tagged here, at the only site that spawns a light, so height adjustment
857 // never moves a light placed by hand in the level (the matched-actor path
858 // above also adds pre-existing actors to TrafficSigns).
861
862 UTrafficLightComponent *TrafficLightComponent = TrafficLight->GetTrafficLightComponent();
863 TrafficLightComponent->SetSignId(SignalId.c_str());
864
865 if (ClosestWaypointToSignal)
866 {
867 auto SignalDistanceToRoad =
868 (GetMap()->ComputeTransform(ClosestWaypointToSignal.value()).location - CarlaTransform.location).Length();
869 double LaneWidth = GetMap()->GetLaneWidth(ClosestWaypointToSignal.value());
870
871 if(SignalDistanceToRoad < LaneWidth * 0.5)
872 {
873 carla::log_warning("Traffic light",
874 TCHAR_TO_UTF8(*TrafficLightComponent->GetSignId()),
875 "overlaps a driving lane. Disabling collision...");
876
877 TArray<UPrimitiveComponent*> Primitives;
878 TrafficLight->GetComponents(Primitives);
879 for (auto* Primitive : Primitives)
880 {
881 Primitive->SetCollisionProfileName(TEXT("NoCollision"));
882 }
883 }
884 }
885
886 RegisterLightComponentFromOpenDRIVE(TrafficLightComponent);
887 TrafficLightComponent->InitializeSign(GetMap().value());
888 }
889}
890
892{
894 check(GM);
895
896 const auto &Signals = GetMap()->GetSignals();
897 for (auto& SignalPair : Signals)
898 {
899 auto &Signal = SignalPair.second;
900 FString SignalType = Signal->GetType().c_str();
901
902 ATrafficSignBase * ClosestTrafficSign = GetClosestTrafficSignActor(*Signal, GetWorld());
903 if (ClosestTrafficSign)
904 {
905 USignComponent *SignComponent;
906 if (SignComponentModels.Contains(SignalType))
907 {
908 SignComponent =
909 NewObject<USignComponent>(
910 ClosestTrafficSign, SignComponentModels[SignalType]);
911 }
912 else
913 {
914 SignComponent =
915 NewObject<USignComponent>(
916 ClosestTrafficSign);
917 }
918 SignComponent->SetSignId(Signal->GetSignalId().c_str());
919 SignComponent->RegisterComponent();
920 SignComponent->AttachToComponent(
921 ClosestTrafficSign->GetRootComponent(),
922 FAttachmentTransformRules::KeepRelativeTransform);
923 TrafficSignComponents.Add(SignComponent->GetSignId(), SignComponent);
924 TrafficSigns.Add(ClosestTrafficSign);
925 }
926 else if (TrafficSignsModels.Contains(SignalType))
927 {
928 // We do not spawn stops painted in the ground
929 if (Signal->GetName() == "Stencil_STOP")
930 {
931 continue;
932 }
933 auto CarlaTransform = Signal->GetTransform();
934 // The signal transform carries the OpenDRIVE zOffset (mounting height
935 // of the plate above the road, e.g. ~2.3 m for Town12 stop signs). The
936 // spawned blueprint models the whole sign with its pivot at the pole
937 // base, so spawn at road level or the sign floats by exactly zOffset.
938 CarlaTransform.location.z -= static_cast<float>(Signal->GetZOffset());
939 FTransform SpawnTransform(CarlaTransform);
940 FVector SpawnLocation = SpawnTransform.GetLocation();
941 FRotator SpawnRotation(SpawnTransform.GetRotation());
942 SpawnRotation.Yaw += 90;
943 // Remove road inclination
944 SpawnRotation.Roll = 0;
945 SpawnRotation.Pitch = 0;
946
947 FActorSpawnParameters SpawnParams;
948 SpawnParams.Owner = this;
949 SpawnParams.SpawnCollisionHandlingOverride =
950 ESpawnActorCollisionHandlingMethod::AlwaysSpawn;
951 SpawnParams.OverrideLevel = GM->GetULevelFromName("TrafficSigns");
952 ATrafficSignBase * TrafficSign = GetWorld()->SpawnActor<ATrafficSignBase>(
953 TrafficSignsModels[SignalType],
954 SpawnLocation,
955 SpawnRotation,
956 SpawnParams);
957
958 USignComponent *SignComponent =
959 NewObject<USignComponent>(TrafficSign, SignComponentModels[SignalType]);
960 SignComponent->SetSignId(Signal->GetSignalId().c_str());
961 SignComponent->RegisterComponent();
962 SignComponent->AttachToComponent(
963 TrafficSign->GetRootComponent(),
964 FAttachmentTransformRules::KeepRelativeTransform);
965 SignComponent->InitializeSign(GetMap().value());
966
967 auto ClosestWaypointToSignal =
968 GetMap()->GetClosestWaypointOnRoad(CarlaTransform.location);
969 if (ClosestWaypointToSignal)
970 {
971 auto SignalDistanceToRoad =
972 (GetMap()->ComputeTransform(ClosestWaypointToSignal.value()).location - CarlaTransform.location).Length();
973 double LaneWidth = GetMap()->GetLaneWidth(ClosestWaypointToSignal.value());
974
975 if(SignalDistanceToRoad < LaneWidth * 0.5)
976 {
977 carla::log_warning("Traffic sign",
978 TCHAR_TO_UTF8(*SignComponent->GetSignId()),
979 "overlaps a driving lane. Disabling collision...");
980
981 TArray<UPrimitiveComponent*> Primitives;
982 TrafficSign->GetComponents(Primitives);
983 for (auto* Primitive : Primitives)
984 {
985 Primitive->SetCollisionProfileName(TEXT("NoCollision"));
986 }
987 }
988 }
989 TrafficSignComponents.Add(SignComponent->GetSignId(), SignComponent);
990 // Only spawned signs are tagged: the matched-actor branch above adds
991 // hand-placed level actors to TrafficSigns and must not be moved.
992 TrafficSign->bGeneratedFromOpenDRIVE = true;
993 TrafficSigns.Add(TrafficSign);
994 SpawnedSigns.Add(TrafficSign);
995 }
996 else if (Signal->GetType() == carla::road::SignalType::MaximumSpeed() &&
997 SpeedLimitModels.Contains(Signal->GetSubtype().c_str()))
998 {
999 // Geo-style switch: the OpenDRIVE subtype is always km/h (SignalType.h StVO
1000 // vocabulary), but a map whose signals carry country="US" should show MUTCD mph
1001 // plates. Convert and snap to the nearest available plate; fall back to the EU
1002 // model when no US plate resolves.
1003 TSubclassOf<AActor> SpeedModel = SpeedLimitModels[Signal->GetSubtype().c_str()];
1004 if (Signal->GetCountry() == "US" && SpeedLimitModels_US.Num() > 0)
1005 {
1006 const float Kmh = FCString::Atof(*FString(Signal->GetSubtype().c_str()));
1007 const float Mph = Kmh / 1.60934f;
1008 FString BestKey;
1009 float BestDiff = TNumericLimits<float>::Max();
1010 for (const auto &Pair : SpeedLimitModels_US)
1011 {
1012 const float Diff = FMath::Abs(FCString::Atof(*Pair.Key) - Mph);
1013 if (Diff < BestDiff)
1014 {
1015 BestDiff = Diff;
1016 BestKey = Pair.Key;
1017 }
1018 }
1019 if (!BestKey.IsEmpty())
1020 {
1021 SpeedModel = SpeedLimitModels_US[BestKey];
1022 }
1023 }
1024 auto CarlaTransform = Signal->GetTransform();
1025 // Spawn at road level: the blueprint pivot is at the pole base (see
1026 // the zOffset note on the traffic-sign branch above).
1027 CarlaTransform.location.z -= static_cast<float>(Signal->GetZOffset());
1028 FTransform SpawnTransform(CarlaTransform);
1029 FVector SpawnLocation = SpawnTransform.GetLocation();
1030 FRotator SpawnRotation(SpawnTransform.GetRotation());
1031 SpawnRotation.Yaw += 90;
1032 // Remove road inclination
1033 SpawnRotation.Roll = 0;
1034 SpawnRotation.Pitch = 0;
1035
1036 FActorSpawnParameters SpawnParams;
1037 SpawnParams.Owner = this;
1038 SpawnParams.SpawnCollisionHandlingOverride =
1039 ESpawnActorCollisionHandlingMethod::AlwaysSpawn;
1040 SpawnParams.OverrideLevel = GM->GetULevelFromName("TrafficSigns");
1041 ATrafficSignBase * TrafficSign = GetWorld()->SpawnActor<ATrafficSignBase>(
1042 SpeedModel,
1043 SpawnLocation,
1044 SpawnRotation,
1045 SpawnParams);
1046
1047 USpeedLimitComponent *SignComponent =
1048 NewObject<USpeedLimitComponent>(TrafficSign);
1049 SignComponent->SetSignId(Signal->GetSignalId().c_str());
1050 SignComponent->RegisterComponent();
1051 SignComponent->AttachToComponent(
1052 TrafficSign->GetRootComponent(),
1053 FAttachmentTransformRules::KeepRelativeTransform);
1054 SignComponent->InitializeSign(GetMap().value());
1055 SignComponent->SetSpeedLimit(Signal->GetValue());
1056
1057 auto ClosestWaypointToSignal =
1058 GetMap()->GetClosestWaypointOnRoad(CarlaTransform.location);
1059 if (ClosestWaypointToSignal)
1060 {
1061 auto SignalDistanceToRoad =
1062 (GetMap()->ComputeTransform(ClosestWaypointToSignal.value()).location - CarlaTransform.location).Length();
1063 double LaneWidth = GetMap()->GetLaneWidth(ClosestWaypointToSignal.value());
1064
1065 if(SignalDistanceToRoad < LaneWidth * 0.5)
1066 {
1067 carla::log_warning("Traffic sign",
1068 TCHAR_TO_UTF8(*SignComponent->GetSignId()),
1069 "overlaps a driving lane. Disabling collision...");
1070
1071 TArray<UPrimitiveComponent*> Primitives;
1072 TrafficSign->GetComponents(Primitives);
1073 for (auto* Primitive : Primitives)
1074 {
1075 Primitive->SetCollisionProfileName(TEXT("NoCollision"));
1076 }
1077 }
1078 }
1079 TrafficSignComponents.Add(SignComponent->GetSignId(), SignComponent);
1080 // Only spawned signs are tagged: the matched-actor branch above adds
1081 // hand-placed level actors to TrafficSigns and must not be moved.
1082 TrafficSign->bGeneratedFromOpenDRIVE = true;
1083 TrafficSigns.Add(TrafficSign);
1084 SpawnedSigns.Add(TrafficSign);
1085 }
1086 }
1087}
1088
1090{
1091 bTrafficLightsFrozen = InFrozen;
1093 {
1094 for (auto& TrafficGroupPair : TrafficGroups)
1095 {
1096 auto& TrafficGroup = TrafficGroupPair.Value;
1097 TrafficGroup->SetFrozenGroup(true);
1098 }
1099 }
1100 else
1101 {
1102 for (auto& TrafficGroupPair : TrafficGroups)
1103 {
1104 auto& TrafficGroup = TrafficGroupPair.Value;
1105 TrafficGroup->SetFrozenGroup(false);
1106 }
1107 }
1108}
1109
1114
1116{
1117 if (TrafficGroups.Contains(JunctionId))
1118 {
1119 return TrafficGroups[JunctionId];
1120 }
1121 return nullptr;
1122}
1123
1124
1126{
1127 if (TrafficControllers.Contains(ControllerId))
1128 {
1129 return TrafficControllers[ControllerId];
1130 }
1131 return nullptr;
1132}
1133
1134USignComponent* ATrafficLightManager::GetTrafficSign(FString SignId)
1135{
1136 if (!TrafficSignComponents.Contains(SignId))
1137 {
1138 return nullptr;
1139 }
1140 return TrafficSignComponents[SignId];
1141}
1142
1144{
1145 TArray<AActor*> Actors;
1146 UGameplayStatics::GetAllActorsOfClass(GetWorld(), AActor::StaticClass(), Actors);
1147
1148 // Detect PropsNode Actor which is the father of all the Props imported from Roadrunner
1149 AActor* PropsNode = nullptr;
1150 for(AActor* Actor : Actors)
1151 {
1152 const FString Name = UKismetSystemLibrary::GetDisplayName(Actor);
1153 if(Name.Equals("PropsNode"))
1154 {
1155 PropsNode = Actor;
1156 break;
1157 }
1158 }
1159
1160 if(PropsNode)
1161 {
1162 PropsNode->GetAttachedActors(Actors, true);
1163 RemoveAttachedProps(Actors);
1164 PropsNode->Destroy();
1165 }
1166
1167}
1168
1169void ATrafficLightManager::RemoveAttachedProps(TArray<AActor*> Actors) const
1170{
1171 for(AActor* Actor : Actors)
1172 {
1173 TArray<AActor*> AttachedActors;
1174 Actor->GetAttachedActors(AttachedActors, true);
1175 RemoveAttachedProps(AttachedActors);
1176 Actor->Destroy();
1177 }
1178}
static bool IsValid(const ACarlaWheeledVehicle *Vehicle)
bool MatchSignalAndActor(const carla::road::Signal &Signal, ATrafficSignBase *ClosestTrafficSign)
static TAutoConsoleVariable< bool > CVarCarlaHideSpawnedSignsWhenPlaced(TEXT("carla.TrafficSigns.HideSpawnedWhenPlaced"), true, TEXT("placed in the level streams in. Their traffic logic stays. Read at level load."), ECVF_Default)
T * GetClosestTrafficSignActor(const carla::road::Signal &Signal, UWorld *World)
Base class for the CARLA Game Mode.
const std::optional< carla::road::Map > & GetMap() const
ULevel * GetULevelFromName(FString LevelName)
A traffic sign baked into a level from the regional sign catalog: a pole and a plate whose material i...
UTrafficLightComponent * GetTrafficLightComponent()
Class which implements the state changing of traffic lights.
void AddController(UTrafficLightController *Controller)
Class In charge of creating and assigning traffic light groups, controllers and components.
TArray< AActor * > GetSignsToIgnoreWhileTracing() const
TSet< TWeakObjectPtr< ATrafficSignBase > > SpawnedSigns
void AdoptModelConfigurationFrom(const ATrafficLightManager &Other)
Copy the authored model configuration (traffic light / sign / speed limit blueprints) from another ma...
TSubclassOf< AActor > TrafficLightModel_RHT
void SuppressSpawnedSignsIfLevelHasPlacedSigns(ULevel *Level)
const std::optional< carla::road::Map > & GetMap()
void RegisterLightComponentFromOpenDRIVE(UTrafficLightComponent *TrafficLight)
void OnLevelAddedToWorld(ULevel *Level, UWorld *World)
TObjectPtr< USceneComponent > SceneComponent
void SetFrozen(bool InFrozen)
FDelegateHandle LevelAddedHandle
TMap< FString, TSubclassOf< AActor > > SpeedLimitModels
TMap< FString, TSubclassOf< USignComponent > > SignComponentModels
ATrafficLightGroup * GetTrafficGroup(int JunctionId)
virtual void Tick(float DeltaSeconds) override
TMap< FString, TObjectPtr< UTrafficLightController > > TrafficControllers
USignComponent * GetTrafficSign(FString SignId)
TMap< FString, TSubclassOf< AActor > > TrafficSignsModels
TSubclassOf< AActor > TrafficLightModel_LHT
void RegisterLightComponentGenerated(UTrafficLightComponent *TrafficLight)
UTrafficLightController * GetController(FString ControllerId)
void RemoveAttachedProps(TArray< AActor * > Actors) const
virtual void EndPlay(const EEndPlayReason::Type EndPlayReason) override
TArray< TObjectPtr< ATrafficSignBase > > TrafficSigns
TMap< FString, TSubclassOf< AActor > > SpeedLimitModels_US
TMap< int, TObjectPtr< ATrafficLightGroup > > TrafficGroups
void SetAdjustSignsHeightToGround(bool bEnabled)
TMap< FString, TObjectPtr< USignComponent > > TrafficSignComponents
bool bGeneratedFromOpenDRIVE
True for signs/lights generated from the OpenDRIVE map.
ETrafficSignState GetTrafficSignState() const
int32 GetSpeedLimitKmh() const
km/h this sign limits to, -1 when it is not a speed-limit sign.
static ACarlaGameModeBase * GetGameMode(const UObject *WorldContextObject)
static void ApplyLaneIdsFromMapLogic(const FString &XODRFilePath, ATrafficLightManager *TrafficLightManager)
Apply lane IDs from map_logic.json to traffic light components.
static FString FindPathToXODRFile(const FString &InMapName)
static FString GetXODR(const UWorld *World)
Return the OpenDrive XML associated to MapName, or empty if the file is not found.
Maps a controller from OpenDrive.
void AddTrafficLight(UTrafficLightComponent *TrafficLight)
static std::optional< road::Map > Load(const std::string &opendrive)
static const std::string YieldSign()
static bool IsTrafficLight(const std::string &type)
static const std::string MaximumSpeed()
static const std::string StopSign()
const std::string & GetSubtype() const
Definition Signal.h:131
const geom::Transform & GetTransform() const
Definition Signal.h:172
const std::string & GetType() const
Definition Signal.h:127
double GetZOffset() const
Definition Signal.h:119
const SignId & GetSignalId() const
Definition Signal.h:85
bool AdjustSignToGround(UWorld *World, ATrafficSignBase *Sign, const TArray< AActor * > &IgnoredActors, const TArray< UPrimitiveComponent * > &IgnoredComponents)
Snap a single traffic sign to the ground and shift its trigger UBoxComponents by the same delta so th...
int32_t JuncId
Definition RoadTypes.h:17
std::string SignId
Definition RoadTypes.h:25
static void log_warning(Args &&... args)
Definition Logging.h:96