12#include "Components/InstancedStaticMeshComponent.h"
13#include "Components/LightComponent.h"
14#include "Components/StaticMeshComponent.h"
15#include "Dom/JsonObject.h"
16#include "Engine/Engine.h"
17#include "Engine/Level.h"
18#include "Engine/World.h"
19#include "EngineUtils.h"
20#include "HAL/FileManager.h"
21#include "JsonObjectConverter.h"
22#include "Kismet/KismetMaterialLibrary.h"
23#include "Materials/MaterialInstanceDynamic.h"
24#include "Materials/MaterialInterface.h"
25#include "Materials/MaterialParameterCollection.h"
26#include "Misc/FileHelper.h"
27#include "Serialization/JsonSerializer.h"
28#include "UObject/UObjectIterator.h"
32#include "LevelEditorViewport.h"
37 TSharedRef<FJsonObject> LoadLightJsonRoot(
const FString& Path)
40 TSharedPtr<FJsonObject> Root;
41 if (FFileHelper::LoadFileToString(InputString, *Path))
43 TSharedRef<TJsonReader<>> Reader = TJsonReaderFactory<>::Create(InputString);
44 FJsonSerializer::Deserialize(Reader, Root);
46 return Root.IsValid() ? Root.ToSharedRef() : MakeShared<FJsonObject>();
49 bool SaveLightJsonRoot(
const TSharedRef<FJsonObject>& Root,
const FString& Path)
52 TSharedRef<TJsonWriter<>> Writer = TJsonWriterFactory<>::Create(&OutputString);
53 if (!FJsonSerializer::Serialize(Root, Writer))
55 IFileManager::Get().MakeDirectory(*FPaths::GetPath(Path),
true);
56 return FFileHelper::SaveStringToFile(OutputString, *Path);
61 TSharedRef<FJsonObject> GetOrCreateLightCatalog(
const TSharedRef<FJsonObject>& Root,
const TCHAR* CatalogKey)
63 const TSharedPtr<FJsonObject>* Existing =
nullptr;
64 if (Root->TryGetObjectField(CatalogKey, Existing) && Existing->IsValid())
65 return Existing->ToSharedRef();
66 TSharedRef<FJsonObject> Catalog = MakeShared<FJsonObject>();
67 Root->SetObjectField(CatalogKey, Catalog);
74 TSharedRef<FJsonObject> Catalog = GetOrCreateLightCatalog(Root, CatalogKey);
76 TSharedPtr<FJsonObject> Object = FJsonObjectConverter::UStructToJsonObject(
Default);
77 if (!Object.IsValid())
79 Catalog->SetObjectField(Key, Object);
84 bool LoadDefault(
FLightAssetDefault& OutDefault,
const FString& Key,
const TCHAR* CatalogKey)
87 const TSharedPtr<FJsonObject>* Catalog =
nullptr;
88 if (!Root->TryGetObjectField(CatalogKey, Catalog) || !Catalog->IsValid())
91 const TSharedPtr<FJsonObject>* Object =
nullptr;
92 if (!(*Catalog)->TryGetObjectField(Key, Object) || !Object->IsValid())
95 return FJsonObjectConverter::JsonObjectToUStruct(Object->ToSharedRef(), &OutDefault);
104 template <
typename FuncT>
107 TArray<UStaticMeshComponent*> MeshComponents;
108 TrafficLight->GetComponents<UStaticMeshComponent>(MeshComponents);
109 for (UStaticMeshComponent* MeshComponent : MeshComponents)
111 const int32 NumMaterials = MeshComponent->GetNumMaterials();
112 for (int32 MaterialIndex = 0; MaterialIndex < NumMaterials; ++MaterialIndex)
114 if (UMaterialInstanceDynamic* MID = Cast<UMaterialInstanceDynamic>(MeshComponent->GetMaterial(MaterialIndex)))
117 if (MID->GetScalarParameterValue(TEXT(
"lightvalue"), Dummy))
126 const FString ProceduralBuildingsKey(TEXT(
"ProceduralBuildings"));
127 const FString LegacyBuildingsKey(TEXT(
"LegacyBuildings"));
134 FString ExtractNumberedFamilyKey(
const FString& MaterialName)
136 for (int32 Idx = 0; Idx < MaterialName.Len(); ++Idx)
138 if (!FChar::IsAlpha(MaterialName[Idx]))
140 if (Idx > 0 && FChar::IsAlpha(MaterialName[Idx - 1]))
144 while (WordEnd < MaterialName.Len() && FChar::IsAlpha(MaterialName[WordEnd]))
146 int32 DigitsEnd = WordEnd;
147 while (DigitsEnd < MaterialName.Len() && FChar::IsDigit(MaterialName[DigitsEnd]))
149 if (DigitsEnd > WordEnd)
150 return MaterialName.Mid(Idx, DigitsEnd - Idx);
172 bool TryGetLightBuildingFamilyKey(
const AActor* Owner, FString& OutFamilyKey)
174 if (Owner ==
nullptr)
177 TArray<UStaticMeshComponent*> MeshComponents;
178 Owner->GetComponents<UStaticMeshComponent>(MeshComponents);
180 FString FirstMatchedMaterialName;
181 for (UStaticMeshComponent* MeshComponent : MeshComponents)
183 const int32 NumMaterials = MeshComponent->GetNumMaterials();
184 for (int32 MaterialIndex = 0; MaterialIndex < NumMaterials; ++MaterialIndex)
186 UMaterialInterface* Material = MeshComponent->GetMaterial(MaterialIndex);
187 if (UMaterialInstanceDynamic* MID = Cast<UMaterialInstanceDynamic>(Material))
188 Material = MID->Parent;
189 if (Material ==
nullptr)
191 if (!Material->GetPathName().StartsWith(TEXT(
"/Game/Carla/Static/Building/")))
194 if (FirstMatchedMaterialName.IsEmpty())
195 FirstMatchedMaterialName = Material->GetName();
197 const FString Family = ExtractNumberedFamilyKey(Material->GetName());
198 if (!Family.IsEmpty())
200 OutFamilyKey = Family;
206 if (!FirstMatchedMaterialName.IsEmpty())
208 OutFamilyKey = FirstMatchedMaterialName;
230 UMaterialInterface* ResolveStaticProceduralBuildingAsset(UMaterialInterface* Material)
232 if (Material ==
nullptr)
235 UMaterialInterface* StaticAsset = Material;
236 if (UMaterialInstanceDynamic* MID = Cast<UMaterialInstanceDynamic>(Material))
238 StaticAsset = MID->Parent;
240 if (StaticAsset ==
nullptr)
243 if (!StaticAsset->GetPathName().StartsWith(TEXT(
"/Game/Carla/Static/Building/")))
245 if (!StaticAsset->GetName().Contains(TEXT(
"FakeInterior")))
249 if (!Material->GetScalarParameterValue(TEXT(
"EmissiveIntensity"), Dummy))
256 struct FBuildingSlotRef
258 TWeakObjectPtr<UStaticMeshComponent> Component;
259 int32 MaterialIndex = 0;
270 template <
typename FuncT>
271 void CollectBuildingSlotsOnActor(
AActor* Actor,
bool bInstancedOnly, FuncT&& Callback)
273 if (Actor ==
nullptr)
276 TArray<UStaticMeshComponent*> MeshComponents;
277 Actor->GetComponents<UStaticMeshComponent>(MeshComponents);
278 TArray<FBuildingSlotRef> Slots;
279 for (UStaticMeshComponent* MeshComponent : MeshComponents)
281 const bool bIsInstanced = MeshComponent->IsA<UInstancedStaticMeshComponent>();
282 if (bIsInstanced != bInstancedOnly)
285 const int32 NumMaterials = MeshComponent->GetNumMaterials();
286 for (int32 MaterialIndex = 0; MaterialIndex < NumMaterials; ++MaterialIndex)
288 if (ResolveStaticProceduralBuildingAsset(MeshComponent->GetMaterial(MaterialIndex)) ==
nullptr)
291 FBuildingSlotRef Slot;
292 Slot.Component = MeshComponent;
293 Slot.MaterialIndex = MaterialIndex;
299 Callback(Actor, Slots);
307 template <
typename FuncT>
308 void CollectBuildingActorsByMechanism(UWorld* World,
bool bInstancedOnly, FuncT&& Callback)
310 for (TActorIterator<AActor> It(World); It; ++It)
312 CollectBuildingSlotsOnActor(*It, bInstancedOnly, Callback);
332 void ApplyToActorSlots(
AActor* ,
const TArray<FBuildingSlotRef>& Slots,
float EmissiveIntensity,
float PercentLit,
const FLinearColor& Color)
334 const float Threshold = FMath::Clamp(PercentLit / 100.0f, 0.0f, 1.0f);
335 for (
const FBuildingSlotRef& Slot : Slots)
337 UStaticMeshComponent* Component = Slot.Component.Get();
338 if (Component ==
nullptr)
341 UMaterialInstanceDynamic* MID = Component->CreateAndSetMaterialInstanceDynamic(Slot.MaterialIndex);
345 MID->SetScalarParameterValue(TEXT(
"EmissiveIntensity"), EmissiveIntensity);
346 MID->SetVectorParameterValue(TEXT(
"Emissive Color"), Color);
347 MID->SetScalarParameterValue(TEXT(
"On/Off"), 1.0f);
348 MID->SetScalarParameterValue(TEXT(
"PercentLitThreshold"), Threshold);
364 float StableActorRandom01(
const AActor* Actor)
366 const uint32 Hash = GetTypeHash(
Actor->GetPathName());
367 return (Hash % 100000) / 100000.0f;
376 void ApplyToLegacyActorSlots(
AActor* Actor,
const TArray<FBuildingSlotRef>& Slots,
float EmissiveIntensity,
float PercentLit,
const FLinearColor& Color)
378 const float Threshold = FMath::Clamp(PercentLit / 100.0f, 0.0f, 1.0f);
379 const bool bLit = StableActorRandom01(Actor) < Threshold;
380 for (
const FBuildingSlotRef& Slot : Slots)
382 UStaticMeshComponent* Component = Slot.Component.Get();
383 if (Component ==
nullptr)
386 UMaterialInstanceDynamic* MID = Component->CreateAndSetMaterialInstanceDynamic(Slot.MaterialIndex);
390 MID->SetScalarParameterValue(TEXT(
"EmissiveIntensity"), bLit ? EmissiveIntensity : 0.0f);
391 MID->SetVectorParameterValue(TEXT(
"Emissive Color"), Color);
392 MID->SetScalarParameterValue(TEXT(
"On/Off"), bLit ? 1.0f : 0.0f);
393 MID->SetScalarParameterValue(TEXT(
"PercentLitThreshold"), 1.0f);
411 const TArray<FString> VehicleLightGroupNames = {
412 TEXT(
"Position"), TEXT(
"Low Beam"), TEXT(
"High Beam"), TEXT(
"Fog"),
413 TEXT(
"Brake"), TEXT(
"Reverse"), TEXT(
"Left Blinker"), TEXT(
"Right Blinker"),
414 TEXT(
"Special1"), TEXT(
"Interior"),
422 constexpr float VehicleLightIntensityDisplayScale = 10000.0f;
428 UMaterialInterface* ResolveVehicleMaterialBase(UMaterialInterface* Material)
430 if (UMaterialInstanceDynamic* MID = Cast<UMaterialInstanceDynamic>(Material))
431 Material = MID->Parent;
432 UMaterialInterface* Base = Material;
433 int32 SafetyCount = 0;
434 while (UMaterialInstance* Instance = Cast<UMaterialInstance>(Base))
436 if (Instance->Parent ==
nullptr || ++SafetyCount > 10)
438 Base = Instance->Parent;
459 struct FVehicleEmissiveSlotRef
461 TWeakObjectPtr<UStaticMeshComponent> Component;
462 int32 MaterialIndex = 0;
465 TArray<FVehicleEmissiveSlotRef> CollectVehicleEmissiveSlots(
AActor* Actor)
467 TArray<FVehicleEmissiveSlotRef> Result;
468 if (Actor ==
nullptr)
471 TArray<UStaticMeshComponent*> MeshComponents;
472 Actor->GetComponents<UStaticMeshComponent>(MeshComponents);
473 for (UStaticMeshComponent* MeshComponent : MeshComponents)
475 const int32 NumMaterials = MeshComponent->GetNumMaterials();
476 for (int32 MaterialIndex = 0; MaterialIndex < NumMaterials; ++MaterialIndex)
478 UMaterialInterface* Base = ResolveVehicleMaterialBase(MeshComponent->GetMaterial(MaterialIndex));
479 if (Base ==
nullptr || !Base->GetName().Contains(TEXT(
"VehicleLightsMaster")))
482 FVehicleEmissiveSlotRef Slot;
483 Slot.Component = MeshComponent;
484 Slot.MaterialIndex = MaterialIndex;
500 bool ComponentNameMatchesGroup(
const FString& ComponentName,
const FString& GroupName)
502 const FString Lower = ComponentName.ToLower();
503 if (GroupName == TEXT(
"Left Blinker"))
504 return Lower.Contains(TEXT(
"blinker")) && Lower.Contains(TEXT(
"-l-"));
505 if (GroupName == TEXT(
"Right Blinker"))
506 return Lower.Contains(TEXT(
"blinker")) && Lower.Contains(TEXT(
"-r-"));
507 if (GroupName == TEXT(
"Low Beam"))
508 return Lower.Contains(TEXT(
"low_beam"));
509 if (GroupName == TEXT(
"High Beam"))
510 return Lower.Contains(TEXT(
"high_beam"));
511 if (GroupName == TEXT(
"Position"))
512 return Lower.Contains(TEXT(
"position")) && !Lower.Contains(TEXT(
"plate"));
513 if (GroupName == TEXT(
"Fog"))
514 return Lower.Contains(TEXT(
"fog"));
515 if (GroupName == TEXT(
"Brake"))
516 return Lower.Contains(TEXT(
"brake"));
517 if (GroupName == TEXT(
"Reverse"))
518 return Lower.Contains(TEXT(
"reverse"));
519 if (GroupName == TEXT(
"Special1"))
520 return Lower.Contains(TEXT(
"special1"));
521 if (GroupName == TEXT(
"Interior"))
522 return Lower.Contains(TEXT(
"interior"));
526 TArray<ULightComponent*> CollectVehicleLightComponentsForGroup(
AActor* Actor,
const FString& GroupName)
528 TArray<ULightComponent*> Result;
529 if (Actor ==
nullptr)
532 TArray<ULightComponent*> AllLights;
533 Actor->GetComponents<ULightComponent>(AllLights);
534 for (ULightComponent* LightComponent : AllLights)
535 if (LightComponent != nullptr && ComponentNameMatchesGroup(LightComponent->GetName(), GroupName))
536 Result.Add(LightComponent);
541 TWeakObjectPtr<AActor> CurrentVehiclePreviewActor;
549 const UEnum* Enum = StaticEnum<ELightType>();
550 FString Name = Enum ? Enum->GetNameStringByValue(
static_cast<int64
>(Group)) : FString();
552 if (Name.FindLastChar(TEXT(
':'), Idx))
553 Name = Name.RightChop(Idx + 1);
554 return Name.IsEmpty() ? TEXT(
"Other") : Name;
559 return SaveDefault(
Default, ClassName, TEXT(
"perClass"));
564 return LoadDefault(OutDefault, ClassName, TEXT(
"perClass"));
586 if (Owner ==
nullptr)
588 bOutIsBuildingClass =
false;
593 if (TryGetLightBuildingFamilyKey(Owner, FamilyKey))
595 bOutIsBuildingClass =
true;
599 bOutIsBuildingClass =
false;
604 return Owner->GetClass()->GetName();
609 TArray<FLightAssetSummary> Result;
611 UWorld* World = GEngine ? GEngine->GetWorldFromContextObject(WorldContextObject, EGetWorldErrorMode::ReturnNull) :
nullptr;
613 if (Subsystem ==
nullptr)
616 TMap<FString, int32> ClassNameToIndex;
617 for (
const auto& Pair : Subsystem->
GetLights())
619 UCarlaLight* Light = Pair.Value;
620 if (Light ==
nullptr || Light->GetOwner() ==
nullptr)
623 bool bIsBuildingClass =
false;
624 const FString ClassName =
GetLightClassKey(Light->GetOwner(), bIsBuildingClass);
625 if (int32* ExistingIndex = ClassNameToIndex.Find(ClassName))
627 Result[*ExistingIndex].InstanceCount++;
643 Summary.
Current = SavedDefault;
647 ClassNameToIndex.Add(ClassName, Result.Num());
660 UWorld* World = GEngine ? GEngine->GetWorldFromContextObject(WorldContextObject, EGetWorldErrorMode::ReturnNull) :
nullptr;
662 if (Subsystem ==
nullptr)
665 for (
const auto& Pair : Subsystem->
GetLights())
667 UCarlaLight* Light = Pair.Value;
668 if (Light ==
nullptr || Light->GetOwner() ==
nullptr)
671 bool bIsBuildingClass =
false;
675 Light->SetLightColor(
Default.Color);
676 Light->SetLightIntensity(
Default.Intensity);
677 Light->SetEmissiveIntensity(
Default.EmissiveIntensity);
684 UWorld* World = GEngine ? GEngine->GetWorldFromContextObject(WorldContextObject, EGetWorldErrorMode::ReturnNull) :
nullptr;
686 if (Subsystem ==
nullptr)
689 for (
const auto& Pair : Subsystem->
GetLights())
691 UCarlaLight* Light = Pair.Value;
692 if (Light ==
nullptr || Light->GetOwner() ==
nullptr)
694 bool bIsBuildingClass =
false;
698 Light->SetLightColor(Value.
Color);
699 Light->SetLightIntensity(Value.
Intensity);
707 if (GEditor ==
nullptr)
710 UWorld* World = GEngine ? GEngine->GetWorldFromContextObject(WorldContextObject, EGetWorldErrorMode::ReturnNull) :
nullptr;
712 if (Subsystem ==
nullptr)
715 for (
const auto& Pair : Subsystem->
GetLights())
717 UCarlaLight* Light = Pair.Value;
718 AActor* Owner = Light ? Light->GetOwner() :
nullptr;
719 if (Owner ==
nullptr)
721 bool bIsBuildingClass =
false;
724 GEditor->SelectActor(Owner, bSelected,
false);
726 GEditor->NoteSelectionChange();
732 TArray<FLightAssetSummary> Result;
734 UWorld* World = GEngine ? GEngine->GetWorldFromContextObject(WorldContextObject, EGetWorldErrorMode::ReturnNull) :
nullptr;
735 if (World ==
nullptr)
738 TMap<FString, int32> ClassNameToIndex;
739 for (TActorIterator<ATrafficLightBase> It(World); It; ++It)
745 const FString ClassName =
TrafficLight->GetClass()->GetName();
746 if (int32* ExistingIndex = ClassNameToIndex.Find(ClassName))
748 Result[*ExistingIndex].InstanceCount++;
760 ForEachTrafficLightSignalMID(
TrafficLight, [&Summary](UMaterialInstanceDynamic* MID)
765 if (MID->GetScalarParameterValue(TEXT(
"lightvalue"), Value))
767 Summary.Current.EmissiveIntensity = Value;
776 Summary.bHasSavedDefault =
true;
779 ClassNameToIndex.Add(ClassName, Result.Num());
792 UWorld* World = GEngine ? GEngine->GetWorldFromContextObject(WorldContextObject, EGetWorldErrorMode::ReturnNull) :
nullptr;
793 if (World ==
nullptr)
796 for (TActorIterator<ATrafficLightBase> It(World); It; ++It)
802 ForEachTrafficLightSignalMID(
TrafficLight, [EmissiveIntensity](UMaterialInstanceDynamic* MID)
804 MID->SetScalarParameterValue(TEXT(
"lightvalue"), EmissiveIntensity);
811 UWorld* World = GEngine ? GEngine->GetWorldFromContextObject(WorldContextObject, EGetWorldErrorMode::ReturnNull) :
nullptr;
812 if (World ==
nullptr)
815 for (TActorIterator<ATrafficLightBase> It(World); It; ++It)
827 MID->SetScalarParameterValue(TEXT(
"lightvalue"),
Default.EmissiveIntensity);
834 UWorld* World = GEngine ? GEngine->GetWorldFromContextObject(WorldContextObject, EGetWorldErrorMode::ReturnNull) :
nullptr;
835 if (World ==
nullptr)
838 const float OnOffValue = bOn ? 1.0f : 0.0f;
839 for (TActorIterator<ATrafficLightBase> It(World); It; ++It)
845 ForEachTrafficLightSignalMID(
TrafficLight, [OnOffValue](UMaterialInstanceDynamic* MID)
847 MID->SetScalarParameterValue(TEXT(
"On/Off"), OnOffValue);
855 if (GEditor ==
nullptr)
858 UWorld* World = GEngine ? GEngine->GetWorldFromContextObject(WorldContextObject, EGetWorldErrorMode::ReturnNull) :
nullptr;
859 if (World ==
nullptr)
862 for (TActorIterator<ATrafficLightBase> It(World); It; ++It)
869 GEditor->NoteSelectionChange();
875 TArray<FLightAssetSummary> Result;
877 UWorld* World = GEngine ? GEngine->GetWorldFromContextObject(WorldContextObject, EGetWorldErrorMode::ReturnNull) :
nullptr;
878 if (World ==
nullptr)
881 int32 ActorCount = 0;
882 float SampleEmissive = 0.0f;
883 bool bFoundSample =
false;
884 CollectBuildingActorsByMechanism(World,
true, [&](
AActor*,
const TArray<FBuildingSlotRef>& Slots)
889 for (
const FBuildingSlotRef& Slot : Slots)
891 UStaticMeshComponent* Component = Slot.Component.Get();
892 UMaterialInterface* Material = Component ? Component->GetMaterial(Slot.MaterialIndex) :
nullptr;
894 if (Material !=
nullptr && Material->GetScalarParameterValue(TEXT(
"EmissiveIntensity"), Value))
896 SampleEmissive = Value;
907 Summary.
ClassName = ProceduralBuildingsKey;
917 Summary.
Current = SavedDefault;
927 UWorld* World = GEngine ? GEngine->GetWorldFromContextObject(WorldContextObject, EGetWorldErrorMode::ReturnNull) :
nullptr;
928 if (World ==
nullptr)
931 CollectBuildingActorsByMechanism(World,
true, [EmissiveIntensity, PercentLit, Color](
AActor* Actor,
const TArray<FBuildingSlotRef>& Slots)
933 ApplyToActorSlots(Actor, Slots, EmissiveIntensity, PercentLit, Color);
948 if (GEditor ==
nullptr)
951 UWorld* World = GEngine ? GEngine->GetWorldFromContextObject(WorldContextObject, EGetWorldErrorMode::ReturnNull) :
nullptr;
952 if (World ==
nullptr)
955 CollectBuildingActorsByMechanism(World,
true, [bSelected](
AActor* Actor,
const TArray<FBuildingSlotRef>&)
957 GEditor->SelectActor(Actor, bSelected,
false);
959 GEditor->NoteSelectionChange();
965 TArray<FLightAssetSummary> Result;
967 UWorld* World = GEngine ? GEngine->GetWorldFromContextObject(WorldContextObject, EGetWorldErrorMode::ReturnNull) :
nullptr;
968 if (World ==
nullptr)
971 int32 ActorCount = 0;
972 float SampleEmissive = 0.0f;
973 bool bFoundSample =
false;
974 CollectBuildingActorsByMechanism(World,
false, [&](
AActor*,
const TArray<FBuildingSlotRef>& Slots)
979 for (
const FBuildingSlotRef& Slot : Slots)
981 UStaticMeshComponent* Component = Slot.Component.Get();
982 UMaterialInterface* Material = Component ? Component->GetMaterial(Slot.MaterialIndex) :
nullptr;
984 if (Material !=
nullptr && Material->GetScalarParameterValue(TEXT(
"EmissiveIntensity"), Value))
986 SampleEmissive = Value;
1007 Summary.
Current = SavedDefault;
1011 Result.Add(Summary);
1017 UWorld* World = GEngine ? GEngine->GetWorldFromContextObject(WorldContextObject, EGetWorldErrorMode::ReturnNull) :
nullptr;
1018 if (World ==
nullptr)
1021 CollectBuildingActorsByMechanism(World,
false, [EmissiveIntensity, PercentLit, Color](
AActor* Actor,
const TArray<FBuildingSlotRef>& Slots)
1023 ApplyToLegacyActorSlots(Actor, Slots, EmissiveIntensity, PercentLit, Color);
1037 if (Level ==
nullptr)
1042 const bool bProcedural =
LoadClassDefault(ProceduralBuildingsKey, Procedural);
1044 if (!bProcedural && !bLegacy)
1047 int32 NumProcedural = 0;
1048 int32 NumLegacy = 0;
1049 for (
AActor* Actor : Level->Actors)
1053 CollectBuildingSlotsOnActor(Actor,
true, [&](
AActor* Owner,
const TArray<FBuildingSlotRef>& Slots)
1061 CollectBuildingSlotsOnActor(Actor,
false, [&](
AActor* Owner,
const TArray<FBuildingSlotRef>& Slots)
1068 if (NumProcedural + NumLegacy > 0)
1070 UE_LOG(LogCarla, Log, TEXT(
"Light defaults: %d instanced and %d legacy building actors in %s"),
1071 NumProcedural, NumLegacy, *Level->GetOuter()->GetName());
1077 FDelegateHandle GBuildingActorsInitializedHandle;
1078 FDelegateHandle GBuildingLevelAddedHandle;
1090 GBuildingActorsInitializedHandle = FWorldDelegates::OnWorldInitializedActors.AddLambda([](
const FActorsInitializedParams& Params)
1092 if (Params.World ==
nullptr || !Params.World->IsGameWorld())
1094 for (ULevel* Level : Params.World->GetLevels())
1097 GBuildingLevelAddedHandle = FWorldDelegates::LevelAddedToWorld.AddLambda([](ULevel* Level, UWorld* World)
1099 if (World !=
nullptr && World->IsGameWorld() && World->AreActorsInitialized())
1106 FWorldDelegates::OnWorldInitializedActors.Remove(GBuildingActorsInitializedHandle);
1107 FWorldDelegates::LevelAddedToWorld.Remove(GBuildingLevelAddedHandle);
1113 if (GEditor ==
nullptr)
1116 UWorld* World = GEngine ? GEngine->GetWorldFromContextObject(WorldContextObject, EGetWorldErrorMode::ReturnNull) :
nullptr;
1117 if (World ==
nullptr)
1120 CollectBuildingActorsByMechanism(World,
false, [bSelected](
AActor* Actor,
const TArray<FBuildingSlotRef>&)
1122 GEditor->SelectActor(Actor, bSelected,
false);
1124 GEditor->NoteSelectionChange();
1130 return VehicleLightGroupNames;
1135 TArray<FVehicleLightSummary> Result;
1137 UWorld* World = GEngine ? GEngine->GetWorldFromContextObject(WorldContextObject, EGetWorldErrorMode::ReturnNull) :
nullptr;
1138 if (World ==
nullptr)
1145 TSharedPtr<FJsonObject> Root;
1146 TSharedRef<TJsonReader<>> Reader = TJsonReaderFactory<>::Create(JsonString);
1147 if (!FJsonSerializer::Deserialize(Reader, Root) || !Root.IsValid())
1150 const TArray<TSharedPtr<FJsonValue>>* VehiclesArray =
nullptr;
1151 if (!Root->TryGetArrayField(TEXT(
"Vehicles"), VehiclesArray))
1154 for (
const TSharedPtr<FJsonValue>& Entry : *VehiclesArray)
1156 const TSharedPtr<FJsonObject>* VehicleObj =
nullptr;
1157 if (!Entry->TryGetObject(VehicleObj) || !VehicleObj->IsValid())
1160 bool bHasLights =
false;
1161 (*VehicleObj)->TryGetBoolField(TEXT(
"HasLights"), bHasLights);
1166 if (!(*VehicleObj)->TryGetStringField(TEXT(
"Class"), ClassPath))
1169 UClass* VehicleClass = LoadObject<UClass>(
nullptr, *ClassPath);
1170 if (VehicleClass ==
nullptr)
1172 UE_LOG(LogCarla, Warning, TEXT(
"ScanVehicleCatalog: failed to load class %s"), *ClassPath);
1182 FActorSpawnParameters SpawnParams;
1183 SpawnParams.SpawnCollisionHandlingOverride = ESpawnActorCollisionHandlingMethod::AlwaysSpawn;
1184 SpawnParams.ObjectFlags |= RF_Transient;
1185 AActor* TempActor = World->SpawnActor<
AActor>(VehicleClass, FVector(0.0f, 0.0f, -1000000.0f), FRotator::ZeroRotator, SpawnParams);
1186 if (TempActor ==
nullptr)
1189 FString Make, Model;
1190 (*VehicleObj)->TryGetStringField(TEXT(
"Make"), Make);
1191 (*VehicleObj)->TryGetStringField(TEXT(
"Model"), Model);
1194 Summary.
ClassName = VehicleClass->GetName();
1195 Summary.
DisplayName = (Make.IsEmpty() && Model.IsEmpty())
1197 : FString::Printf(TEXT(
"%s %s"), *Make, *Model);
1199 for (
const FString& GroupName : VehicleLightGroupNames)
1207 bool bFoundIntensity =
false;
1208 for (
const FVehicleEmissiveSlotRef& Slot : CollectVehicleEmissiveSlots(TempActor))
1210 UStaticMeshComponent* Component = Slot.Component.Get();
1211 UMaterialInterface* Material = Component ? Component->GetMaterial(Slot.MaterialIndex) :
nullptr;
1212 if (Material ==
nullptr || bFoundIntensity)
1216 if (Material->GetScalarParameterValue(TEXT(
"Intensity"), Value))
1219 bFoundIntensity =
true;
1227 TArray<ULightComponent*> AllLightComponents;
1228 TempActor->GetComponents<ULightComponent>(AllLightComponents);
1229 for (ULightComponent* LightComponent : AllLightComponents)
1231 if (LightComponent ==
nullptr)
1233 for (
const FString& GroupName : VehicleLightGroupNames)
1235 if (!ComponentNameMatchesGroup(LightComponent->GetName(), GroupName))
1239 Summary.
GroupIntensity.Add(GroupName, LightComponent->Intensity / VehicleLightIntensityDisplayScale);
1243 float SavedIntensity = Summary.
Intensity;
1244 TMap<FString, float> SavedGroups;
1248 for (
const TPair<FString, float>& Pair : SavedGroups)
1254 TempActor->Destroy();
1255 Result.Add(Summary);
1275 void ApplyVehicleLightsToActor(
AActor* Actor,
float Intensity,
const TMap<FString, float>& GroupIntensity)
1277 if (Actor ==
nullptr)
1284 for (
const FVehicleEmissiveSlotRef& Slot : CollectVehicleEmissiveSlots(Actor))
1286 UStaticMeshComponent* Component = Slot.Component.Get();
1287 if (Component ==
nullptr)
1290 UMaterialInstanceDynamic* MID = Component->CreateAndSetMaterialInstanceDynamic(Slot.MaterialIndex);
1294 for (
const FString& GroupName : VehicleLightGroupNames)
1295 MID->SetScalarParameterValue(*GroupName, Intensity);
1301 for (
const TPair<FString, float>& Pair : GroupIntensity)
1303 for (ULightComponent* LightComponent : CollectVehicleLightComponentsForGroup(
Actor, Pair.Key))
1305 LightComponent->SetIntensity(Pair.Value * VehicleLightIntensityDisplayScale);
1313 UWorld* World = GEngine ? GEngine->GetWorldFromContextObject(WorldContextObject, EGetWorldErrorMode::ReturnNull) :
nullptr;
1314 if (World ==
nullptr)
1317 for (TActorIterator<AActor> It(World); It; ++It)
1320 if (Actor ==
nullptr || Actor->GetClass()->GetName() != ClassName)
1322 ApplyVehicleLightsToActor(Actor, Intensity, GroupIntensity);
1328 ApplyVehicleLightsToActor(Actor, Intensity, GroupIntensity);
1333 if (Actor ==
nullptr)
1336 for (
const FVehicleEmissiveSlotRef& Slot : CollectVehicleEmissiveSlots(Actor))
1338 UStaticMeshComponent* Component = Slot.Component.Get();
1339 if (Component ==
nullptr)
1342 UMaterialInstanceDynamic* MID = Component->CreateAndSetMaterialInstanceDynamic(Slot.MaterialIndex);
1346 for (
const FString& GroupName : VehicleLightGroupNames)
1348 const float* Value = MaterialGroupValue.Find(GroupName);
1349 MID->SetScalarParameterValue(*GroupName, Value !=
nullptr ? *Value : 0.0f);
1353 for (
const TPair<FString, float>& Pair : RealLightGroupIntensity)
1355 for (ULightComponent* LightComponent : CollectVehicleLightComponentsForGroup(Actor, Pair.Key))
1357 LightComponent->SetIntensity(Pair.Value * VehicleLightIntensityDisplayScale);
1365 TSharedRef<FJsonObject> Catalog = GetOrCreateLightCatalog(Root, TEXT(
"perVehicleClass"));
1367 TSharedRef<FJsonObject> Entry = MakeShared<FJsonObject>();
1368 Entry->SetNumberField(TEXT(
"Intensity"), Intensity);
1369 TSharedRef<FJsonObject> GroupsObj = MakeShared<FJsonObject>();
1370 for (
const TPair<FString, float>& Pair : GroupIntensity)
1371 GroupsObj->SetNumberField(Pair.Key, Pair.Value);
1372 Entry->SetObjectField(TEXT(
"GroupIntensity"), GroupsObj);
1373 Catalog->SetObjectField(ClassName, Entry);
1381 const TSharedPtr<FJsonObject>* Catalog =
nullptr;
1382 if (!Root->TryGetObjectField(TEXT(
"perVehicleClass"), Catalog) || !Catalog->IsValid())
1385 const TSharedPtr<FJsonObject>* Entry =
nullptr;
1386 if (!(*Catalog)->TryGetObjectField(ClassName, Entry) || !Entry->IsValid())
1389 double IntensityValue = OutIntensity;
1390 (*Entry)->TryGetNumberField(TEXT(
"Intensity"), IntensityValue);
1391 OutIntensity =
static_cast<float>(IntensityValue);
1393 const TSharedPtr<FJsonObject>* GroupsObj =
nullptr;
1394 if ((*Entry)->TryGetObjectField(TEXT(
"GroupIntensity"), GroupsObj) && GroupsObj->IsValid())
1396 for (
const auto& Pair : (*GroupsObj)->Values)
1399 if (Pair.Value->TryGetNumber(Value))
1400 OutGroupIntensity.Add(FString(*Pair.Key),
static_cast<float>(Value));
1409 const TSharedPtr<FJsonObject>* Catalog =
nullptr;
1410 if (!Root->TryGetObjectField(TEXT(
"perVehicleClass"), Catalog) || !Catalog->IsValid())
1413 for (
const auto& Pair : (*Catalog)->Values)
1415 const FString ClassName(*Pair.Key);
1416 float Intensity = 1.0f;
1417 TMap<FString, float> GroupIntensity;
1426 UWorld* World = GEngine ? GEngine->GetWorldFromContextObject(WorldContextObject, EGetWorldErrorMode::ReturnNull) :
nullptr;
1427 if (World ==
nullptr || GEditor ==
nullptr)
1432 UClass* VehicleClass =
nullptr;
1433 for (TObjectIterator<UClass> ClassIt; ClassIt; ++ClassIt)
1435 if (ClassIt->GetName() == ClassName)
1437 VehicleClass = *ClassIt;
1441 if (VehicleClass ==
nullptr)
1444 FLevelEditorViewportClient* ViewportClient = GCurrentLevelEditingViewportClient;
1445 if (ViewportClient ==
nullptr)
1451 const FVector CameraLocation = ViewportClient->GetViewLocation();
1452 const FVector TraceStart(CameraLocation.X, CameraLocation.Y, CameraLocation.Z + 2000.0f);
1453 const FVector TraceEnd(CameraLocation.X, CameraLocation.Y, CameraLocation.Z - 100000.0f);
1456 FCollisionQueryParams QueryParams;
1457 QueryParams.bTraceComplex =
true;
1458 if (!World->LineTraceSingleByChannel(Hit, TraceStart, TraceEnd, ECC_WorldStatic, QueryParams))
1461 const FVector SpawnLocation = Hit.Location + FVector(0.0f, 0.0f, 50.0f);
1462 FActorSpawnParameters SpawnParams;
1463 SpawnParams.SpawnCollisionHandlingOverride = ESpawnActorCollisionHandlingMethod::AlwaysSpawn;
1464 AActor* Spawned = World->SpawnActor<
AActor>(VehicleClass, SpawnLocation, FRotator::ZeroRotator, SpawnParams);
1465 if (Spawned ==
nullptr)
1468 CurrentVehiclePreviewActor = Spawned;
1473 const FVector CamOffset(-700.0f, -500.0f, 350.0f);
1474 const FVector CamLocation = SpawnLocation + CamOffset;
1475 ViewportClient->SetViewLocation(CamLocation);
1476 ViewportClient->SetViewRotation((SpawnLocation - CamLocation).Rotation());
1477 ViewportClient->Invalidate();
1488 if (
AActor* Existing = CurrentVehiclePreviewActor.Get())
1489 Existing->Destroy();
1490 CurrentVehiclePreviewActor =
nullptr;
1496 UWorld* World = GEngine ? GEngine->GetWorldFromContextObject(WorldContextObject, EGetWorldErrorMode::ReturnNull) :
nullptr;
1497 if (World ==
nullptr)
1499 UMaterialParameterCollection* Collection = LoadObject<UMaterialParameterCollection>(
1500 nullptr, TEXT(
"/Game/Carla/Blueprints/Game/CarlaParameters.CarlaParameters"));
1501 if (Collection ==
nullptr)
1503 UKismetMaterialLibrary::SetScalarParameterValue(World, Collection, TEXT(
"VisualTime"), 0.25f);
std::vector< carla::rpc::LightState > GetLights(FString Client)
static TArray< FVehicleLightSummary > ScanVehicleCatalog(const UObject *WorldContextObject)
static void ReapplyLegacyBuildingsToLevel(const UObject *WorldContextObject)
static void PreviewVehicleBlinkers(const UObject *WorldContextObject)
static AActor * SpawnVehiclePreview(const UObject *WorldContextObject, const FString &ClassName)
static void SetLegacyBuildingsSelected(const UObject *WorldContextObject, bool bSelected)
static void RegisterRuntimeBuildingDefaultsHooks()
static void ApplyVehicleLightsRuntimeState(AActor *Actor, const TMap< FString, float > &MaterialGroupValue, const TMap< FString, float > &RealLightGroupIntensity)
static void SetTrafficLightClassPreviewOn(const UObject *WorldContextObject, const FString &ClassName, bool bOn)
static bool SaveClassDefault(const FString &ClassName, const FLightAssetDefault &Default)
static void ApplyLightDefaultToClassLive(const UObject *WorldContextObject, const FString &ClassName, const FLightAssetDefault &Value)
static FString GetLightGroupName(ELightType Group)
static FString GetLightDefaultsPath()
static bool LoadGroupDefault(ELightType Group, UPARAM(ref) FLightAssetDefault &OutDefault)
static FString GetLightClassKey(AActor *Owner, bool &bOutIsBuildingClass)
static TArray< FLightAssetSummary > ScanLegacyBuildingsInLevel(const UObject *WorldContextObject)
static bool GetLightDefault(const FString &ClassName, ELightType Group, UPARAM(ref) FLightAssetDefault &OutDefault)
static void ReapplyProceduralBuildingsToLevel(const UObject *WorldContextObject)
static FString GetVehicleParametersPath()
static void ApplyProceduralBuildingsEmissiveLive(const UObject *WorldContextObject, float EmissiveIntensity, float PercentLit, const FLinearColor &Color)
static void ApplyTrafficLightDefaultToClassLive(const UObject *WorldContextObject, const FString &ClassName, float EmissiveIntensity)
static bool SaveGroupDefault(ELightType Group, const FLightAssetDefault &Default)
static void ApplyVehicleLightsToSingleActor(AActor *Actor, float Intensity, const TMap< FString, float > &GroupIntensity)
static void SetTrafficLightClassActorsSelected(const UObject *WorldContextObject, const FString &ClassName, bool bSelected)
static void UnregisterRuntimeBuildingDefaultsHooks()
static bool LoadVehicleLightDefault(const FString &ClassName, UPARAM(ref) float &OutIntensity, UPARAM(ref) TMap< FString, float > &OutGroupIntensity)
static void ReapplyLightDefaultsToLevel(const UObject *WorldContextObject)
static TArray< FLightAssetSummary > ScanTrafficLightAssetsInLevel(const UObject *WorldContextObject)
static TArray< FLightAssetSummary > ScanLightAssetsInLevel(const UObject *WorldContextObject)
static bool LoadClassDefault(const FString &ClassName, UPARAM(ref) FLightAssetDefault &OutDefault)
static void ReapplyTrafficLightDefaultsToLevel(const UObject *WorldContextObject)
static void ClearVehiclePreview(const UObject *WorldContextObject)
static void ReapplyBuildingDefaultsToLevel(ULevel *Level)
static void ApplyLegacyBuildingsEmissiveLive(const UObject *WorldContextObject, float EmissiveIntensity, float PercentLit, const FLinearColor &Color)
static TArray< FString > GetVehicleLightGroupNames()
static TArray< FLightAssetSummary > ScanProceduralBuildingsInLevel(const UObject *WorldContextObject)
static void ReapplyVehicleLightsToLevel(const UObject *WorldContextObject)
static void SetProceduralBuildingsSelected(const UObject *WorldContextObject, bool bSelected)
static void ApplyVehicleLightsLive(const UObject *WorldContextObject, const FString &ClassName, float Intensity, const TMap< FString, float > &GroupIntensity)
static void SetClassActorsSelected(const UObject *WorldContextObject, const FString &ClassName, bool bSelected)
static bool SaveVehicleLightDefault(const FString &ClassName, float Intensity, const TMap< FString, float > &GroupIntensity)
carla::SharedPtr< cc::Actor > Actor
FLightAssetDefault Current
TMap< FString, bool > GroupPresent
TMap< FString, float > GroupIntensity