66#include < wx/textctrl.h>
77#include < wx/button.h>
88#include < wx/bmpbuttn.h>
9+ #include < wx/checkbox.h>
10+ #include < wx/choice.h>
911
1012#include " i18n.h"
13+ #include " imap.h"
14+ #include " ilayer.h"
1115#include " wxutil/Bitmap.h"
1216#include " ui/materials/MaterialChooser.h"
1317#include " ui/materials/MaterialSelector.h"
@@ -22,15 +26,29 @@ DecalShooterPanel::DecalShooterPanel(wxWindow* parent) :
2226 _widthCtrl (nullptr ),
2327 _heightCtrl (nullptr ),
2428 _offsetCtrl (nullptr ),
29+ _rotationCtrl (nullptr ),
30+ _randomRotationCheckbox (nullptr ),
2531 _materialEntry (nullptr ),
26- _browseButton (nullptr )
32+ _browseButton (nullptr ),
33+ _autogroupCheckbox (nullptr ),
34+ _flipCheckbox (nullptr ),
35+ _layerChoice (nullptr )
2736{
2837 _instance = this ;
2938 populateWindow ();
39+
40+ _mapEventConnection = GlobalMapModule ().signal_mapEvent ().connect (
41+ sigc::mem_fun (*this , &DecalShooterPanel::onMapEvent)
42+ );
43+
44+ connectToMapRoot ();
3045}
3146
3247DecalShooterPanel::~DecalShooterPanel ()
3348{
49+ _mapEventConnection.disconnect ();
50+ _layersChangedConnection.disconnect ();
51+
3452 if (_instance == this )
3553 {
3654 _instance = nullptr ;
@@ -57,24 +75,93 @@ double DecalShooterPanel::getDecalOffset() const
5775 return _offsetCtrl ? _offsetCtrl->GetValue () : 0.125 ;
5876}
5977
78+ double DecalShooterPanel::getDecalRotation () const
79+ {
80+ return _rotationCtrl ? _rotationCtrl->GetValue () : 0.0 ;
81+ }
82+
83+ bool DecalShooterPanel::isRandomRotationEnabled () const
84+ {
85+ return _randomRotationCheckbox ? _randomRotationCheckbox->GetValue () : false ;
86+ }
87+
88+ bool DecalShooterPanel::isFlipEnabled () const
89+ {
90+ return _flipCheckbox ? _flipCheckbox->GetValue () : false ;
91+ }
92+
6093std::string DecalShooterPanel::getDecalMaterial () const
6194{
6295 return _materialEntry ? _materialEntry->GetValue ().ToStdString () : " textures/common/decal" ;
6396}
6497
98+ bool DecalShooterPanel::isAutogroupEnabled () const
99+ {
100+ return _autogroupCheckbox ? _autogroupCheckbox->GetValue () : false ;
101+ }
102+
103+ int DecalShooterPanel::getSelectedLayerId () const
104+ {
105+ if (!_layerChoice || _layerChoice->GetSelection () == wxNOT_FOUND)
106+ {
107+ return -1 ;
108+ }
109+
110+ return static_cast <int >(reinterpret_cast <intptr_t >(_layerChoice->GetClientData (_layerChoice->GetSelection ())));
111+ }
112+
113+ void DecalShooterPanel::onDecalCreated (const scene::INodePtr& decalNode)
114+ {
115+ if (!decalNode)
116+ {
117+ return ;
118+ }
119+
120+ auto mapRoot = GlobalMapModule ().getRoot ();
121+ if (!mapRoot)
122+ {
123+ return ;
124+ }
125+
126+ // Do the layer assignment
127+ int selectedLayerId = getSelectedLayerId ();
128+ if (selectedLayerId >= 0 )
129+ {
130+ decalNode->moveToLayer (selectedLayerId);
131+ }
132+
133+ if (isAutogroupEnabled ())
134+ {
135+ // Create a new group if we dont have one
136+ if (!_currentSessionGroup)
137+ {
138+ _currentSessionGroup = mapRoot->getSelectionGroupManager ().createSelectionGroup ();
139+ }
140+
141+ _currentSessionGroup->addNode (decalNode);
142+ }
143+ }
144+
145+ void DecalShooterPanel::resetSessionGroup ()
146+ {
147+ _currentSessionGroup.reset ();
148+ }
149+
65150void DecalShooterPanel::onPanelActivated ()
66151{
152+ connectToMapRoot ();
67153}
68154
69155void DecalShooterPanel::onPanelDeactivated ()
70156{
157+ resetSessionGroup ();
71158}
72159
73160void DecalShooterPanel::populateWindow ()
74161{
75162 SetSizer (new wxBoxSizer (wxVERTICAL));
76163
77- wxFlexGridSizer* gridSizer = new wxFlexGridSizer (4 , 2 , 6 , 12 );
164+ wxFlexGridSizer* gridSizer = new wxFlexGridSizer (6 , 2 , 6 , 12 );
78165 gridSizer->AddGrowableCol (1 );
79166
80167 wxStaticText* widthLabel = new wxStaticText (this , wxID_ANY, _ (" Width:" ));
@@ -105,6 +192,26 @@ void DecalShooterPanel::populateWindow()
105192 gridSizer->Add (offsetLabel, 0 , wxALIGN_CENTER_VERTICAL);
106193 gridSizer->Add (_offsetCtrl, 1 , wxEXPAND);
107194
195+ wxStaticText* rotationLabel = new wxStaticText (this , wxID_ANY, _ (" Rotation:" ));
196+
197+ wxBoxSizer* rotationSizer = new wxBoxSizer (wxHORIZONTAL);
198+ _rotationCtrl = new wxSpinCtrlDouble (this , wxID_ANY);
199+ _rotationCtrl->SetRange (-180.0 , 180.0 );
200+ _rotationCtrl->SetValue (0.0 );
201+ _rotationCtrl->SetIncrement (15.0 );
202+ _rotationCtrl->SetDigits (1 );
203+ _rotationCtrl->SetToolTip (_ (" Rotation angle in degrees" ));
204+
205+ _randomRotationCheckbox = new wxCheckBox (this , wxID_ANY, _ (" Random" ));
206+ _randomRotationCheckbox->SetToolTip (_ (" Apply a random rotation for each decal" ));
207+ _randomRotationCheckbox->Bind (wxEVT_CHECKBOX, &DecalShooterPanel::onRandomRotationToggled, this );
208+
209+ rotationSizer->Add (_rotationCtrl, 1 , wxEXPAND | wxRIGHT, 4 );
210+ rotationSizer->Add (_randomRotationCheckbox, 0 , wxALIGN_CENTER_VERTICAL);
211+
212+ gridSizer->Add (rotationLabel, 0 , wxALIGN_CENTER_VERTICAL);
213+ gridSizer->Add (rotationSizer, 1 , wxEXPAND);
214+
108215 wxStaticText* materialLabel = new wxStaticText (this , wxID_ANY, _ (" Material:" ));
109216
110217 wxBoxSizer* materialSizer = new wxBoxSizer (wxHORIZONTAL);
@@ -122,8 +229,30 @@ void DecalShooterPanel::populateWindow()
122229 gridSizer->Add (materialLabel, 0 , wxALIGN_CENTER_VERTICAL);
123230 gridSizer->Add (materialSizer, 1 , wxEXPAND);
124231
232+ // Layer choice
233+ wxStaticText* layerLabel = new wxStaticText (this , wxID_ANY, _ (" Layer:" ));
234+ _layerChoice = new wxChoice (this , wxID_ANY);
235+ _layerChoice->SetToolTip (_ (" Assign created decals to this layer" ));
236+ populateLayerChoice ();
237+ gridSizer->Add (layerLabel, 0 , wxALIGN_CENTER_VERTICAL);
238+ gridSizer->Add (_layerChoice, 1 , wxEXPAND);
239+
125240 GetSizer ()->Add (gridSizer, 0 , wxEXPAND | wxALL, 12 );
126241
242+ // Checkboxes row
243+ wxBoxSizer* checkboxSizer = new wxBoxSizer (wxHORIZONTAL);
244+
245+ _autogroupCheckbox = new wxCheckBox (this , wxID_ANY, _ (" Autogroup" ));
246+ _autogroupCheckbox->SetToolTip (_ (" When enabled, all decals placed during this tool session will be grouped together" ));
247+ _autogroupCheckbox->Bind (wxEVT_CHECKBOX, &DecalShooterPanel::onAutogroupToggled, this );
248+ checkboxSizer->Add (_autogroupCheckbox, 0 , wxRIGHT, 12 );
249+
250+ _flipCheckbox = new wxCheckBox (this , wxID_ANY, _ (" Flip" ));
251+ _flipCheckbox->SetToolTip (_ (" Flip the decal. Useful for decals facing the wrong direction." ));
252+ checkboxSizer->Add (_flipCheckbox, 0 );
253+
254+ GetSizer ()->Add (checkboxSizer, 0 , wxLEFT | wxRIGHT | wxBOTTOM, 12 );
255+
127256 wxStaticText* helpText = new wxStaticText (this , wxID_ANY,
128257 _ (" Use Ctrl+Shift+Middle-Click\n in the 3D view to place decals." ));
129258 helpText->SetForegroundColour (wxSystemSettings::GetColour (wxSYS_COLOUR_GRAYTEXT));
@@ -137,4 +266,91 @@ void DecalShooterPanel::onBrowseMaterial(wxCommandEvent& ev)
137266 chooser->Destroy ();
138267}
139268
269+ void DecalShooterPanel::onAutogroupToggled (wxCommandEvent& ev)
270+ {
271+ if (!_autogroupCheckbox->GetValue ())
272+ {
273+ resetSessionGroup ();
274+ }
275+ }
276+
277+ void DecalShooterPanel::onRandomRotationToggled (wxCommandEvent& ev)
278+ {
279+ // Disable the rotation text field when randomizer is enabled
280+ if (_rotationCtrl)
281+ {
282+ _rotationCtrl->Enable (!_randomRotationCheckbox->GetValue ());
283+ }
284+ }
285+
286+ void DecalShooterPanel::populateLayerChoice ()
287+ {
288+ if (!_layerChoice)
289+ {
290+ return ;
291+ }
292+
293+ int previousSelection = getSelectedLayerId ();
294+
295+ _layerChoice->Clear ();
296+ _layerChoice->Append (_ (" None" ), reinterpret_cast <void *>(static_cast <intptr_t >(-1 )));
297+
298+ auto mapRoot = GlobalMapModule ().getRoot ();
299+ if (mapRoot)
300+ {
301+ mapRoot->getLayerManager ().foreachLayer ([this ](int layerId, const std::string& layerName)
302+ {
303+ _layerChoice->Append (layerName, reinterpret_cast <void *>(static_cast <intptr_t >(layerId)));
304+ });
305+ }
306+
307+ if (previousSelection >= 0 )
308+ {
309+ for (unsigned int i = 0 ; i < _layerChoice->GetCount (); ++i)
310+ {
311+ int layerId = static_cast <int >(reinterpret_cast <intptr_t >(_layerChoice->GetClientData (i)));
312+ if (layerId == previousSelection)
313+ {
314+ _layerChoice->SetSelection (i);
315+ return ;
316+ }
317+ }
318+ }
319+
320+ _layerChoice->SetSelection (0 );
321+ }
322+
323+ void DecalShooterPanel::onLayersChanged ()
324+ {
325+ populateLayerChoice ();
326+ }
327+
328+ void DecalShooterPanel::connectToMapRoot ()
329+ {
330+ _layersChangedConnection.disconnect ();
331+
332+ auto mapRoot = GlobalMapModule ().getRoot ();
333+ if (mapRoot)
334+ {
335+ _layersChangedConnection = mapRoot->getLayerManager ().signal_layersChanged ().connect (
336+ sigc::mem_fun (*this , &DecalShooterPanel::onLayersChanged)
337+ );
338+ }
339+
340+ populateLayerChoice ();
341+ }
342+
343+ void DecalShooterPanel::onMapEvent (IMap::MapEvent ev)
344+ {
345+ if (ev == IMap::MapLoaded)
346+ {
347+ connectToMapRoot ();
348+ }
349+ else if (ev == IMap::MapUnloading)
350+ {
351+ _layersChangedConnection.disconnect ();
352+ resetSessionGroup ();
353+ }
354+ }
355+
140356} // namespace ui
0 commit comments