CARLA
Loading...
Searching...
No Matches
LightDefaultsEditorPanel.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#if WITH_EDITOR
10
11#include "CoreMinimal.h"
13
14#include <util/ue-header-guard-begin.h>
15#include "Widgets/SCompoundWidget.h"
16#include "Widgets/Views/SListView.h"
17#include <util/ue-header-guard-end.h>
18
19// Native (non-Blueprint) editor tool window: scans every light-bearing
20// Blueprint class registered in the currently open level (see
21// ULightDefaultsJsonUtils::ScanLightAssetsInLevel, deduplicated by class),
22// lets an artist edit color/intensity/emissive per class, and saves the
23// result to Config/Lights/Defaults.json. A pure C++ Slate panel instead of an
24// Editor Utility Widget Blueprint -- UE's Python editor API has WidgetTree
25// (a Blueprint's design-time widget tree) marked BlueprintProtected, so this
26// tool cannot be authored headlessly; building it natively in C++ sidesteps
27// that entirely and needs no manual Blueprint wiring.
28class SLightDefaultsPanel : public SCompoundWidget
29{
30public:
31 SLATE_BEGIN_ARGS(SLightDefaultsPanel) {}
32 SLATE_END_ARGS()
33
34 void Construct(const FArguments& InArgs);
35
36private:
37 // Full scan results (every group); Rows is the subset matching
38 // ActiveFilter, bound to the ListView -- this is what makes the group
39 // buttons behave like tabs without needing four separate lists/scans.
40 // AllTrafficRows is a separate, ungrouped scan (ATrafficLightBase, no
41 // UCarlaLight involved) shown instead of Rows-from-AllRows when the
42 // Traffic Lights tab is active -- see bShowingTrafficLights.
43 // AllBuildingRows is a third such ungrouped scan (baked, non-Blueprint
44 // AActor instances on HISM components, no UCarlaLight, no per-class
45 // dedup possible -- see ULightDefaultsJsonUtils::ScanProceduralBuildingsInLevel),
46 // and AllLegacyBuildingRows a fourth (the same FakeInterior material
47 // family but on plain, non-HISM components -- pre-modular Blueprint
48 // buildings and standalone placed pieces, found across Town01-07).
49 // Both are shown together -- one row each -- when the Buildings tab is
50 // active, see bShowingBuildings.
51 TArray<TSharedPtr<FLightAssetSummary>> AllRows;
52 TArray<TSharedPtr<FLightAssetSummary>> AllTrafficRows;
53 TArray<TSharedPtr<FLightAssetSummary>> AllBuildingRows;
54 TArray<TSharedPtr<FLightAssetSummary>> AllLegacyBuildingRows;
55 TArray<TSharedPtr<FLightAssetSummary>> Rows;
56 TSharedPtr<SListView<TSharedPtr<FLightAssetSummary>>> RowsListView;
57 ELightType ActiveFilter = ELightType::Street;
58 bool bShowingTrafficLights = false;
59 bool bShowingBuildings = false;
60
61 // Vehicles: a fundamentally different shape (see FVehicleLightSummary --
62 // no Color, a global Intensity plus up to 8 per-light-group fields
63 // instead of one Emissive), so it gets its own item type and ListView
64 // rather than being squeezed into Rows/RowsListView above. Shown via a
65 // SWidgetSwitcher slot instead of the ApplyFilter subset trick the other
66 // tabs use, see bShowingVehicles.
67 TArray<TSharedPtr<FVehicleLightSummary>> AllVehicleRows;
68 TSharedPtr<SListView<TSharedPtr<FVehicleLightSummary>>> VehicleRowsListView;
69 bool bShowingVehicles = false;
70
71 FReply OnScanClicked();
72 FReply OnReapplyClicked();
73 FReply OnSaveAllClicked();
74 void ApplyFilter();
75
76 // Pushes Item->Current onto every live instance of its class in the level
77 // (ULightDefaultsJsonUtils::ApplyLightDefaultToClassLive) -- does NOT
78 // touch Defaults.json. Called after every in-row edit so a change is
79 // visible immediately; OnSaveAllClicked is the separate, deliberate step
80 // that commits every row's Current to disk.
81 void ApplyRowLive(TSharedPtr<FLightAssetSummary> Item);
82
83 // Selects/deselects every placed actor of ClassName in the level, so an
84 // artist can see which actors a row is editing.
85 void SetClassSelected(const FString& ClassName, bool bSelected);
86
87 // Traffic-light equivalents of the two above (ULightDefaultsJsonUtils'
88 // Traffic* functions) -- separate because traffic lights aren't found via
89 // UCarlaLightSubsystem, and only Emissive (-> "lightvalue") is meaningful
90 // for them; Intensity/Color stay unused for this tab.
91 void ApplyTrafficRowLive(TSharedPtr<FLightAssetSummary> Item);
92 void SetTrafficClassSelected(const FString& ClassName, bool bSelected);
93
94 // Forces every traffic light of ClassName's signal MIDs' "On/Off" to 1/0,
95 // live only, never saved -- lets an artist preview a lit signal without
96 // starting Play (see ULightDefaultsJsonUtils::SetTrafficLightClassPreviewOn).
97 void SetTrafficClassPreviewOn(const FString& ClassName, bool bOn);
98
99 // Buildings equivalents -- a single global row (no per-class dedup, see
100 // AllBuildingRows), so there's no ClassName to key by: these apply to
101 // every baked building in the level unconditionally.
102 void ApplyBuildingRowLive(TSharedPtr<FLightAssetSummary> Item);
103 void SetBuildingSelected(bool bSelected);
104
105 // Legacy (non-HISM) buildings equivalents -- same single-global-row idea
106 // as the two above, over AllLegacyBuildingRows.
107 void ApplyLegacyBuildingRowLive(TSharedPtr<FLightAssetSummary> Item);
108 void SetLegacyBuildingSelected(bool bSelected);
109
110 TSharedRef<class ITableRow> OnGenerateRow(
111 TSharedPtr<FLightAssetSummary> Item,
112 const TSharedRef<class STableViewBase>& OwnerTable);
113
114 TSharedRef<SWidget> MakeGroupTab(ELightType Group, const FText& Label);
115 TSharedRef<SWidget> MakeTrafficTab();
116 TSharedRef<SWidget> MakeBuildingsTab();
117 TSharedRef<SWidget> MakeVehiclesTab();
118
119 // Pushes Item's Intensity/GroupIntensity onto every actor of ClassName
120 // currently in the world (placed instances and/or a live Preview spawn --
121 // ULightDefaultsJsonUtils::ApplyVehicleLightsLive treats both the same,
122 // see there). Does NOT touch Defaults.json.
123 void ApplyVehicleRowLive(TSharedPtr<FVehicleLightSummary> Item);
124
125 // The Vehicles tab's equivalent of Select: spawns (or destroys, on
126 // toggle-off) one instance of ClassName via
127 // ULightDefaultsJsonUtils::SpawnVehiclePreview/ClearVehiclePreview and
128 // reframes the viewport camera on it, since most rows have no placed
129 // instance to jump to (this tab lists the full catalog, not a level
130 // scan).
131 void ToggleVehiclePreview(const FString& ClassName, bool bPreviewOn);
132
133 TSharedRef<class ITableRow> OnGenerateVehicleRow(
134 TSharedPtr<FVehicleLightSummary> Item,
135 const TSharedRef<class STableViewBase>& OwnerTable);
136};
137
138// Registers/unregisters the "Light Defaults" nomad tab (Window menu). Called
139// from FCarlaModule::StartupModule/ShutdownModule.
140void RegisterLightDefaultsEditorTab();
141void UnregisterLightDefaultsEditorTab();
142
143// Registers/unregisters a hook on FEditorDelegates::OnMapOpened that
144// reapplies the saved Light Defaults catalog (both UCarlaLight-based and
145// traffic-light) shortly after a map finishes loading. Necessary because a
146// light-bearing actor's own registration-time
147// apply (UCarlaLight::RegisterLight, which runs this same session regardless
148// of this tool) creates its emissive MID too early: confirmed via
149// instrumented testing that a same-tick material change made there gets
150// silently reset by something later in map load (World Partition streaming
151// settling, most likely) before the component's real render proxy is built.
152// Waiting for the concrete "map opened" signal, plus a short extra delay,
153// reapplies once everything has actually settled.
154void RegisterLightDefaultsMapOpenedHook();
155void UnregisterLightDefaultsMapOpenedHook();
156
157#endif // WITH_EDITOR
ELightType
Definition CarlaLight.h:30