CARLA
Loading...
Searching...
No Matches
LightDefaultsJsonUtils.h
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
7#pragma once
8
9#include "CoreMinimal.h"
11#include "Kismet/BlueprintFunctionLibrary.h"
12#include "Misc/Paths.h"
13#include "LightDefaultsJsonUtils.generated.h"
14
15class ULevel;
16
17// Everything BP_Lights' Details panel exposes per instance: the inherited
18// UCarlaLight color/intensity plus its own EmissiveIntensity. One of these
19// lives per light-bearing Blueprint class (e.g. "BP_StreetLight01_C") or per
20// LightGroup fallback, in Config/Lights/Defaults.json.
21USTRUCT(BlueprintType)
23{
24 GENERATED_BODY()
25
26 UPROPERTY(EditAnywhere, BlueprintReadWrite)
27 FLinearColor Color = FLinearColor::White;
28
29 UPROPERTY(EditAnywhere, BlueprintReadWrite, meta = (ClampMin = "0.0"))
30 float Intensity = 0.0f;
31
32 UPROPERTY(EditAnywhere, BlueprintReadWrite, meta = (ClampMin = "0.0"))
33 float EmissiveIntensity = 0.0f;
34};
35
36// One row of the scan results a light-defaults editor widget lists: every
37// unique light-bearing Blueprint class currently registered in the level,
38// deduplicated (50 instances of the same streetlamp show once), pre-filled
39// with its saved default if it has one, or a live sample off its first
40// instance to start from otherwise.
41USTRUCT(BlueprintType)
43{
44 GENERATED_BODY()
45
46 UPROPERTY(BlueprintReadOnly)
47 FString ClassName;
48
49 UPROPERTY(BlueprintReadOnly)
51
52 UPROPERTY(BlueprintReadOnly)
53 int32 InstanceCount = 0;
54
55 UPROPERTY(BlueprintReadWrite)
57
58 UPROPERTY(BlueprintReadOnly)
59 bool bHasSavedDefault = false;
60
61 // True when ClassName is actually a building-material family key (see
62 // GetLightClassKey), not a raw Blueprint class name -- lets the panel
63 // route this row into the Buildings tab regardless of LightGroup.
64 UPROPERTY(BlueprintReadOnly)
65 bool bIsBuildingClass = false;
66};
67
68// One row of the Vehicles tab. Unlike every other group here, vehicles have
69// no single UCarlaLight to key a Color/Intensity/Emissive triad on -- a
70// Blueprint like BP_LincolnMKZ places 18 separate real light components
71// (front-low_beam-l-1, back-brake-l-1, ...) AND a shared "*_Emissive"
72// MaterialInstance exposing 8 named scalar parameters mirroring those same
73// groups (confirmed identical, same order, across Lincoln/MiniCooper/
74// Ambulance) plus one standalone global "Intensity" param that turned out to
75// do nothing visible on its own. Two controls, two different targets:
76// - Intensity (this struct's field): writes the SAME value into every
77// exposed group parameter on the Emissive material at once (Position,
78// Low Beam, ... all glow together) -- a "light them all up" shortcut,
79// not the material's own unused "Intensity" param.
80// - GroupIntensity (per column): the REAL Spot/Rect/Point light
81// components' physical Intensity, matched by name (see
82// CollectVehicleLightComponentsForGroup in the .cpp) -- actual scene
83// illumination, independent of the material glow above.
84USTRUCT(BlueprintType)
86{
87 GENERATED_BODY()
88
89 // The Blueprint class name (e.g. "BP_LincolnMKZ_C") -- apply/select key,
90 // matched against AActor::GetClass()->GetName() same as everywhere else.
91 UPROPERTY(BlueprintReadOnly)
92 FString ClassName;
93
94 // "Lincoln Mkz" -- Make + Model from VehicleParameters.json, shown
95 // instead of the raw class name (which the Class column still shows).
96 UPROPERTY(BlueprintReadOnly)
97 FString DisplayName;
98
99 // How many instances of this class are currently placed in the level --
100 // normally 0, since this tab lists the full catalog (VehicleParameters.json),
101 // not a level scan. Placed instances (or a live Preview spawn) still
102 // receive ApplyVehicleLightsLive same as everywhere else.
103 UPROPERTY(BlueprintReadOnly)
104 int32 InstanceCount = 0;
105
106 // Global "light every exposed Emissive group up to this value at once"
107 // control -- see the struct comment above. Sampled at scan time from the
108 // material's own (otherwise-unused) "Intensity" param as a starting
109 // value only; ApplyVehicleLightsLive gives it its real meaning.
110 UPROPERTY(BlueprintReadWrite)
111 float Intensity = 1.0f;
112
113 // Canonical group name (VehicleLightGroupNames) -> that group's real
114 // light components' physical Intensity, current/edited value -- scaled
115 // down by VehicleLightIntensityDisplayScale (10000) from the raw UE5
116 // photometric lux the component actually stores (real values run
117 // ~500000; awkward to type/read directly), so this field runs roughly
118 // 10-100 instead. Only groups this vehicle actually HAS matching
119 // components for are meaningful to apply; see GroupPresent for which
120 // those are.
121 UPROPERTY(BlueprintReadWrite)
122 TMap<FString, float> GroupIntensity;
123
124 // Canonical group name -> whether this vehicle has any real light
125 // component matching that group's naming convention (checked once at
126 // scan time, see CollectVehicleLightComponentsForGroup). The panel greys
127 // out/disables a group's field when this is false instead of showing a
128 // control that would silently do nothing.
129 UPROPERTY(BlueprintReadOnly)
130 TMap<FString, bool> GroupPresent;
131
132 UPROPERTY(BlueprintReadOnly)
133 bool bHasSavedDefault = false;
134};
135
136UCLASS()
138{
139 GENERATED_BODY()
140
141public:
142
143 // Config/Lights/Defaults.json:
144 // { "perClass": { "BP_StreetLight01_C": {...} },
145 // "perGroup": { "Street": {...}, "Building": {...}, ... } }
146 // A class-specific entry wins; a LightGroup entry is the fallback for any
147 // class with no entry of its own (see GetLightDefault).
148 UFUNCTION(BlueprintCallable, Category = "Lights|JSON")
149 static bool SaveClassDefault(const FString& ClassName, const FLightAssetDefault& Default);
150
151 UFUNCTION(BlueprintCallable, Category = "Lights|JSON")
152 static bool LoadClassDefault(const FString& ClassName, UPARAM(ref) FLightAssetDefault& OutDefault);
153
154 UFUNCTION(BlueprintCallable, Category = "Lights|JSON")
155 static bool SaveGroupDefault(ELightType Group, const FLightAssetDefault& Default);
156
157 UFUNCTION(BlueprintCallable, Category = "Lights|JSON")
158 static bool LoadGroupDefault(ELightType Group, UPARAM(ref) FLightAssetDefault& OutDefault);
159
160 // ClassName's own catalog entry if it has one, else Group's fallback
161 // entry. False if neither exists -- caller should leave the light as
162 // authored.
163 UFUNCTION(BlueprintCallable, Category = "Lights|JSON")
164 static bool GetLightDefault(const FString& ClassName, ELightType Group, UPARAM(ref) FLightAssetDefault& OutDefault);
165
166 // Every UCarlaLight registered in WorldContextObject's world (editor or
167 // Play), deduplicated by GetLightClassKey (Blueprint class, or a shared
168 // building-material family for rows with bIsBuildingClass) -- see
169 // FLightAssetSummary. Backs the light-defaults editor widget's list.
170 UFUNCTION(BlueprintCallable, Category = "Lights|JSON", meta = (WorldContext = "WorldContextObject"))
171 static TArray<FLightAssetSummary> ScanLightAssetsInLevel(const UObject* WorldContextObject);
172
173 // Dedup/lookup key for a UCarlaLight owner: normally its exact Blueprint
174 // class name, but if any of its StaticMeshComponent materials lives
175 // under /Game/Carla/Static/Building/ (the same content path the baked/
176 // legacy buildings detection uses), this is a real light-driven building
177 // -- e.g. BP_Apt20_A_C..F_C, which differ only by floor count/shape but
178 // share the same MI_Apt20* material family. In that case the key is the
179 // family token pulled from that material's own name ("Apt20"), so all
180 // the floor/shape variants collapse into one row instead of one each.
181 // Falls back to the raw class name if no building material is found, or
182 // if a building material doesn't follow the "Apt<N>" naming pattern.
183 UFUNCTION(BlueprintCallable, BlueprintPure, Category = "Lights|JSON")
184 static FString GetLightClassKey(AActor* Owner, bool& bOutIsBuildingClass);
185
186 // Pushes each registered light's resolved default (class, else group)
187 // back onto it. Lets the widget preview a saved change immediately,
188 // without restarting Play or reloading the level.
189 UFUNCTION(BlueprintCallable, Category = "Lights|JSON", meta = (WorldContext = "WorldContextObject"))
190 static void ReapplyLightDefaultsToLevel(const UObject* WorldContextObject);
191
192 // Applies Value directly to every registered light whose owner class is
193 // ClassName -- WITHOUT touching Config/Lights/Defaults.json. Lets the
194 // editor widget preview an edit live in the level before an artist
195 // decides to commit it via SaveClassDefault.
196 UFUNCTION(BlueprintCallable, Category = "Lights|JSON", meta = (WorldContext = "WorldContextObject"))
197 static void ApplyLightDefaultToClassLive(const UObject* WorldContextObject, const FString& ClassName, const FLightAssetDefault& Value);
198
199 // Selects (bSelected=true) or deselects (false) every placed actor in the
200 // level that owns a registered light of ClassName, so an artist can see
201 // at a glance which actors a row in the editor widget is editing.
202 // Editor-only; a no-op in packaged builds.
203 UFUNCTION(BlueprintCallable, Category = "Lights|JSON", meta = (WorldContext = "WorldContextObject"))
204 static void SetClassActorsSelected(const UObject* WorldContextObject, const FString& ClassName, bool bSelected);
205
206 // ELightType's UMETA DisplayName ("Street", "Building", ...) -- the key
207 // GetLightDefault/SaveGroupDefault/LoadGroupDefault use in "perGroup".
208 UFUNCTION(BlueprintCallable, BlueprintPure, Category = "Lights|JSON")
209 static FString GetLightGroupName(ELightType Group);
210
211 // Traffic lights (ATrafficLightBase) don't own a UCarlaLight -- their
212 // color/on-off cycling is driven entirely by Blueprint
213 // (BP_TLOpenDrive_RHT/LHT), toggling "On/Off" at runtime on
214 // MaterialInstanceDynamics its own Construction Script already created
215 // (RedLights/YellowLights/GreenLights arrays, kept by reference). The
216 // "lightvalue" scalar (signal brightness when lit) is set once at
217 // construction and never touched again, so it's safe to override --
218 // reuses FLightAssetDefault/FLightAssetSummary and the same
219 // Defaults.json "perClass" catalog (EmissiveIntensity == lightvalue;
220 // Color/Intensity unused for this group). Deduplicated by Blueprint
221 // class, same as ScanLightAssetsInLevel.
222 UFUNCTION(BlueprintCallable, Category = "Lights|JSON", meta = (WorldContext = "WorldContextObject"))
223 static TArray<FLightAssetSummary> ScanTrafficLightAssetsInLevel(const UObject* WorldContextObject);
224
225 // Applies EmissiveIntensity (-> "lightvalue") directly to every traffic
226 // light of ClassName in the level, WITHOUT touching Defaults.json. Never
227 // creates a new MaterialInstanceDynamic -- only writes onto the MID
228 // already assigned to each signal mesh slot, since BP_TLOpenDrive keeps
229 // its own array references to those exact objects for its On/Off
230 // toggling; replacing the slot's material would desync that array from
231 // what's actually rendered and silently break the red/yellow/green
232 // cycling.
233 UFUNCTION(BlueprintCallable, Category = "Lights|JSON", meta = (WorldContext = "WorldContextObject"))
234 static void ApplyTrafficLightDefaultToClassLive(const UObject* WorldContextObject, const FString& ClassName, float EmissiveIntensity);
235
236 // Pushes every traffic light's saved "perClass" EmissiveIntensity (->
237 // "lightvalue") back onto it, same idea as ReapplyLightDefaultsToLevel.
238 UFUNCTION(BlueprintCallable, Category = "Lights|JSON", meta = (WorldContext = "WorldContextObject"))
239 static void ReapplyTrafficLightDefaultsToLevel(const UObject* WorldContextObject);
240
241 // Forces "On/Off" to 1 (bOn) or 0 on every traffic light of ClassName's
242 // signal MIDs, live only -- never saved to Defaults.json. A preview aid
243 // so an artist can see a signal lit without starting Play (the traffic
244 // simulation is what normally drives this parameter).
245 UFUNCTION(BlueprintCallable, Category = "Lights|JSON", meta = (WorldContext = "WorldContextObject"))
246 static void SetTrafficLightClassPreviewOn(const UObject* WorldContextObject, const FString& ClassName, bool bOn);
247
248 // Selects/deselects every placed ATrafficLightBase actor of ClassName --
249 // the traffic-light equivalent of SetClassActorsSelected (which only
250 // looks at UCarlaLightSubsystem and would never find these).
251 UFUNCTION(BlueprintCallable, Category = "Lights|JSON", meta = (WorldContext = "WorldContextObject"))
252 static void SetTrafficLightClassActorsSelected(const UObject* WorldContextObject, const FString& ClassName, bool bSelected);
253
254 // Buildings baked by the modular building tool (BP_BuildingConverterToCode)
255 // are placed as plain, non-Blueprint AActor instances referencing Content
256 // Browser HierarchicalInstancedStaticMeshComponent modules directly --
257 // there's no Blueprint class to key a per-class default on like every
258 // other group here. Detected purely by material instead: any HISM mesh
259 // slot whose static asset lives under /Game/Carla/Static/Building/ and
260 // whose name contains "FakeInterior" (the window/interior-illusion
261 // family -- the shared M_MaterialMaster base underneath is used by far
262 // more than windows, see ResolveStaticProceduralBuildingAsset). Exposed
263 // as a single synthetic "ProceduralBuildings" class covering every baked
264 // building in the level -- one global emissive control until per-style
265 // grouping (by material family) proves necessary.
266 //
267 // Only instanced (ISM/HISM) slots -- see ScanLegacyBuildingsInLevel for the same
268 // material family on plain (non-instanced) StaticMeshComponents, which
269 // can't be controlled the same way.
270 //
271 // A HISM component's material slot is shared by every physical instance
272 // it draws (some hold hundreds of windows at once -- confirmed no
273 // per-instance custom data exists to vary through C++), so per-window
274 // on/off can't be decided here. Instead M_FakeInterior's own graph gates
275 // its emissive output with a PerInstanceRandom node compared against a
276 // "PercentLitThreshold" parameter -- PercentLit (0..100) is just handed
277 // down as that threshold (divided by 100), and the renderer resolves the
278 // per-instance randomness deterministically per physical window.
279 // Color goes to M_FakeInterior's "Emissive Color" (the material ships
280 // white, which reads as cold fluorescent blocks at night).
281 UFUNCTION(BlueprintCallable, Category = "Lights|JSON", meta = (WorldContext = "WorldContextObject"))
282 static TArray<FLightAssetSummary> ScanProceduralBuildingsInLevel(const UObject* WorldContextObject);
283
284 UFUNCTION(BlueprintCallable, Category = "Lights|JSON", meta = (WorldContext = "WorldContextObject"))
285 static void ApplyProceduralBuildingsEmissiveLive(const UObject* WorldContextObject, float EmissiveIntensity, float PercentLit, const FLinearColor& Color);
286
287 UFUNCTION(BlueprintCallable, Category = "Lights|JSON", meta = (WorldContext = "WorldContextObject"))
288 static void ReapplyProceduralBuildingsToLevel(const UObject* WorldContextObject);
289
290 UFUNCTION(BlueprintCallable, Category = "Lights|JSON", meta = (WorldContext = "WorldContextObject"))
291 static void SetProceduralBuildingsSelected(const UObject* WorldContextObject, bool bSelected);
292
293 // Same FakeInterior material family as ScanProceduralBuildingsInLevel,
294 // but on plain (non-HISM) StaticMeshComponents: pre-modular buildings
295 // (BP_Apt19/BP_Apt12/... Blueprint classes) and standalone placed
296 // pieces (StaticMeshActor "SM_Apartment_v0X..."), the kind found across
297 // Town01-07, not just the modular-tool-baked Town10 style. Percent Lit
298 // here picks what fraction of whole BUILDINGS are lit (not windows,
299 // since a plain component has no per-window granularity to gate) -- via
300 // a stable per-actor coin flip in C++, see StableActorRandom01 in the
301 // .cpp. (PercentLitThreshold + PerInstanceRandom, the mechanism the
302 // baked/HISM path above uses, was tried here first and confirmed live
303 // in-editor NOT to vary across legacy actors -- every building snapped
304 // on together instead of a graduated percentage.)
305 UFUNCTION(BlueprintCallable, Category = "Lights|JSON", meta = (WorldContext = "WorldContextObject"))
306 static TArray<FLightAssetSummary> ScanLegacyBuildingsInLevel(const UObject* WorldContextObject);
307
308 UFUNCTION(BlueprintCallable, Category = "Lights|JSON", meta = (WorldContext = "WorldContextObject"))
309 static void ApplyLegacyBuildingsEmissiveLive(const UObject* WorldContextObject, float EmissiveIntensity, float PercentLit, const FLinearColor& Color);
310
311 UFUNCTION(BlueprintCallable, Category = "Lights|JSON", meta = (WorldContext = "WorldContextObject"))
312 static void ReapplyLegacyBuildingsToLevel(const UObject* WorldContextObject);
313
314 UFUNCTION(BlueprintCallable, Category = "Lights|JSON", meta = (WorldContext = "WorldContextObject"))
315 static void SetLegacyBuildingsSelected(const UObject* WorldContextObject, bool bSelected);
316
317 // The saved "ProceduralBuildings" and "LegacyBuildings" defaults, applied
318 // to the actors of one level only (a streaming level or a World
319 // Partition cell) -- what the runtime hooks below call per level.
320 static void ReapplyBuildingDefaultsToLevel(ULevel* Level);
321
322 // Applies the building defaults to game worlds (PIE, -game, server) as
323 // their levels come in, see the .cpp. Registered by the Carla module.
324 static void RegisterRuntimeBuildingDefaultsHooks();
325 static void UnregisterRuntimeBuildingDefaultsHooks();
326
327 // The full vehicle catalog (Config/VehicleParameters.json -- every model
328 // the vehicle factory can spawn, "HasLights" ones only), not a level
329 // scan: most rows have InstanceCount 0 on a fresh map. One row per
330 // Blueprint class, sampling its shared Emissive material's parameters
331 // (see FVehicleLightSummary). Needs a world to spawn into: a vehicle
332 // Blueprint assembles its meshes/materials in its Construction Script,
333 // which never runs on the bare class default object, so the CDO's own
334 // component list comes back empty -- confirmed headless (24/24 vehicles
335 // scanned with zero groups present via the CDO alone). Each class is
336 // spawned far below the map, inspected, and destroyed immediately, all
337 // within this one call -- nothing renders a frame in between.
338 UFUNCTION(BlueprintCallable, Category = "Lights|JSON", meta = (WorldContext = "WorldContextObject"))
339 static TArray<FVehicleLightSummary> ScanVehicleCatalog(const UObject* WorldContextObject);
340
341 // Applies Intensity/GroupIntensity to every actor of ClassName currently
342 // in WorldContextObject's world -- covers both any manually placed
343 // instances AND a live Preview spawn (see SpawnVehiclePreview), since
344 // both are just actors of that class once they exist. WITHOUT touching
345 // Config/Lights/Defaults.json.
346 UFUNCTION(BlueprintCallable, Category = "Lights|JSON", meta = (WorldContext = "WorldContextObject"))
347 static void ApplyVehicleLightsLive(const UObject* WorldContextObject, const FString& ClassName, float Intensity, const TMap<FString, float>& GroupIntensity);
348
349 // Same apply as ApplyVehicleLightsLive, but on exactly Actor -- no
350 // ClassName match, no world sweep. ACarlaWheeledVehicle::BeginPlay uses
351 // this (not the class-name version above) so that N already-spawned
352 // vehicles of one class don't each get rescanned/rewritten every time
353 // another vehicle of that same class spawns.
354 UFUNCTION(BlueprintCallable, Category = "Lights|JSON")
355 static void ApplyVehicleLightsToSingleActor(AActor* Actor, float Intensity, const TMap<FString, float>& GroupIntensity);
356
357 // Gameplay integration, distinct from the two functions above: those
358 // apply Intensity uniformly to every exposed emissive group at once
359 // (correct for the Light Defaults editor Preview, which is meant to show
360 // everything lit for editing). Real gameplay must not do that -- a
361 // group's emissive must only glow when that group is actually ON per
362 // FVehicleLightState. MaterialGroupValue must already have that gating
363 // baked in by the caller (0 for OFF groups, Intensity for ON ones) --
364 // this only pushes it to the material. RealLightGroupIntensity is applied
365 // unconditionally, same as ApplyVehicleLightsToSingleActor, since the
366 // physical light components' own Active/Visibility already gates
367 // rendering. Call this again after every FVehicleLightState change (not
368 // just once at BeginPlay) since RefreshLightState's own Blueprint graph
369 // is opaque to C++ and may repush its own values on every toggle -- this
370 // must run after it so our saved values win.
371 UFUNCTION(BlueprintCallable, Category = "Lights|JSON")
372 static void ApplyVehicleLightsRuntimeState(AActor* Actor, const TMap<FString, float>& MaterialGroupValue, const TMap<FString, float>& RealLightGroupIntensity);
373
374 UFUNCTION(BlueprintCallable, Category = "Lights|JSON")
375 static bool SaveVehicleLightDefault(const FString& ClassName, float Intensity, const TMap<FString, float>& GroupIntensity);
376
377 UFUNCTION(BlueprintCallable, Category = "Lights|JSON")
378 static bool LoadVehicleLightDefault(const FString& ClassName, UPARAM(ref) float& OutIntensity, UPARAM(ref) TMap<FString, float>& OutGroupIntensity);
379
380 // Applies every saved "perVehicleClass" entry to matching actors
381 // currently in the world (placed instances and/or a live Preview spawn) --
382 // the vehicle-catalog equivalent of ReapplyLightDefaultsToLevel, called
383 // from the same "Reapply To Level" button.
384 UFUNCTION(BlueprintCallable, Category = "Lights|JSON", meta = (WorldContext = "WorldContextObject"))
385 static void ReapplyVehicleLightsToLevel(const UObject* WorldContextObject);
386
387 // Spawns one instance of ClassName for visual preview: raycasts straight
388 // down from the active editor viewport camera's current XY to find the
389 // road/ground under it (whatever the artist last navigated to -- no
390 // fixed world coordinate needed, and it stays put across Preview clicks
391 // as long as the viewport doesn't move), spawns there, then reframes the
392 // viewport camera on it. Destroys any previously spawned preview first --
393 // at most one at a time. Returns the spawned actor (or null if the
394 // raycast found no ground/road within range). Editor-only; a no-op in
395 // packaged builds.
396 UFUNCTION(BlueprintCallable, Category = "Lights|JSON", meta = (WorldContext = "WorldContextObject"))
397 static AActor* SpawnVehiclePreview(const UObject* WorldContextObject, const FString& ClassName);
398
399 // Destroys the current preview spawn, if any, without spawning a new
400 // one -- used when a Preview toggle is switched off.
401 UFUNCTION(BlueprintCallable, Category = "Lights|JSON", meta = (WorldContext = "WorldContextObject"))
402 static void ClearVehiclePreview(const UObject* WorldContextObject);
403
404 // Canonical, fixed set of light-group column names every vehicle's
405 // Emissive material is checked against (see FVehicleLightSummary). A
406 // BlueprintPure getter so the panel doesn't duplicate this list.
407 UFUNCTION(BlueprintCallable, BlueprintPure, Category = "Lights|JSON")
408 static TArray<FString> GetVehicleLightGroupNames();
409
410 // Blinkers are gated by Sine('VisualTime') in M_VehicleLightsMaster --
411 // 'VisualTime' is a "CarlaParameters" Material Parameter Collection
412 // scalar that only advances during a running simulation/Play, so a
413 // blinker's Left/Right Blinker parameter (this tool's own column) can
414 // sit permanently invisible in a static editor viewport if that clock
415 // happens to be parked on a negative half of the sine wave -- confirmed:
416 // manually setting VisualTime to 0.1-0.4 or 1.1-1.4 makes it visible.
417 // Pins it to 0.25 (middle of the first confirmed-visible window) so
418 // Preview can actually show blinker glow without Play running.
419 UFUNCTION(BlueprintCallable, Category = "Lights|JSON", meta = (WorldContext = "WorldContextObject"))
420 static void PreviewVehicleBlinkers(const UObject* WorldContextObject);
421
422 static FString GetLightDefaultsPath()
423 {
424 return FPaths::ProjectContentDir() / TEXT("Carla/Config/Lights/Defaults.json");
425 }
426
428 {
429 return FPaths::ProjectContentDir() / TEXT("Carla/Config/VehicleParameters.json");
430 }
431};
ELightType
Definition CarlaLight.h:30
static FString GetVehicleParametersPath()