1 // NeL - MMORPG Framework <http://dev.ryzom.com/projects/nel/>
2 // Copyright (C) 2010 Winch Gate Property Limited
4 // This source file has been modified by the following contributors:
5 // Copyright (C) 2019 Jan BOON (Kaetemi) <jan.boon@kaetemi.be>
7 // This program is free software: you can redistribute it and/or modify
8 // it under the terms of the GNU Affero General Public License as
9 // published by the Free Software Foundation, either version 3 of the
10 // License, or (at your option) any later version.
12 // This program is distributed in the hope that it will be useful,
13 // but WITHOUT ANY WARRANTY; without even the implied warranty of
14 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 // GNU Affero General Public License for more details.
17 // You should have received a copy of the GNU Affero General Public License
18 // along with this program. If not, see <http://www.gnu.org/licenses/>.
20 // vegetable_density_page.cpp : implementation file
24 #include "object_viewer.h"
25 #include "vegetable_density_page.h"
26 #include "vegetable_edit_tools.h"
27 #include "vegetable_noise_value_dlg.h"
28 #include "vegetable_dlg.h"
29 #include "nel/misc/noise_value.h"
30 #include "nel/3d/vegetable.h"
34 #define NL_VEGETABLE_EDIT_ANGLE_SLIDER_SIZE 256
37 /////////////////////////////////////////////////////////////////////////////
38 // CVegetableDensityPage property page
40 IMPLEMENT_DYNCREATE(CVegetableDensityPage
, CPropertyPage
)
42 CVegetableDensityPage::CVegetableDensityPage() : CPropertyPage(CVegetableDensityPage::IDD
),
43 _DensityDlg(NULL
), _MaxDensityDlg(NULL
)
45 //{{AFX_DATA_INIT(CVegetableDensityPage)
49 CVegetableDensityPage::~CVegetableDensityPage()
51 #define REMOVE_WND(wnd) if (wnd) { wnd->DestroyWindow(); delete wnd; }
52 REMOVE_WND(_DensityDlg
);
53 REMOVE_WND(_MaxDensityDlg
);
56 void CVegetableDensityPage::DoDataExchange(CDataExchange
* pDX
)
58 CPropertyPage::DoDataExchange(pDX
);
59 //{{AFX_DATA_MAP(CVegetableDensityPage)
60 DDX_Control(pDX
, IDC_STATIC_VEGETABLE_SHAPENAME
, StaticVegetableShape
);
61 DDX_Control(pDX
, IDC_STATIC_MAX_DENSITY
, MaxDensityStaticText
);
62 DDX_Control(pDX
, IDC_SLIDER_ANGLE_MIN
, AngleMinSlider
);
63 DDX_Control(pDX
, IDC_SLIDER_ANGLE_MAX
, AngleMaxSlider
);
64 DDX_Control(pDX
, IDC_EDIT_ANGLE_MIN
, AngleMinEdit
);
65 DDX_Control(pDX
, IDC_EDIT_ANGLE_MAX
, AngleMaxEdit
);
66 DDX_Control(pDX
, IDC_COMBO_DIST_TYPE
, DistTypeCombo
);
67 DDX_Control(pDX
, IDC_CHECK_MAX_DENSITY
, MaxDensityCheckBox
);
72 BEGIN_MESSAGE_MAP(CVegetableDensityPage
, CPropertyPage
)
73 //{{AFX_MSG_MAP(CVegetableDensityPage)
74 ON_BN_CLICKED(IDC_CHECK_MAX_DENSITY
, OnCheckMaxDensity
)
75 ON_CBN_SELCHANGE(IDC_COMBO_DIST_TYPE
, OnSelchangeComboDistType
)
76 ON_BN_CLICKED(IDC_RADIO_ANGLE_CEILING
, OnRadioAngleCeiling
)
77 ON_BN_CLICKED(IDC_RADIO_ANGLE_FLOOR
, OnRadioAngleFloor
)
78 ON_BN_CLICKED(IDC_RADIO_ANGLE_WALL
, OnRadioAngleWall
)
79 ON_NOTIFY(NM_RELEASEDCAPTURE
, IDC_SLIDER_ANGLE_MAX
, OnReleasedcaptureSliderAngleMax
)
80 ON_NOTIFY(NM_RELEASEDCAPTURE
, IDC_SLIDER_ANGLE_MIN
, OnReleasedcaptureSliderAngleMin
)
81 ON_EN_KILLFOCUS(IDC_EDIT_ANGLE_MIN
, OnKillfocusEditAngleMin
)
82 ON_EN_KILLFOCUS(IDC_EDIT_ANGLE_MAX
, OnKillfocusEditAngleMax
)
83 ON_EN_CHANGE(IDC_EDIT_ANGLE_MIN
, OnChangeEditAngleMin
)
84 ON_EN_CHANGE(IDC_EDIT_ANGLE_MAX
, OnChangeEditAngleMax
)
85 ON_BN_CLICKED(IDC_BUTTON_VEGETABLE_BROWSE
, OnButtonVegetableBrowse
)
90 // ***************************************************************************
91 // ***************************************************************************
92 // ***************************************************************************
93 // ***************************************************************************
96 // ***************************************************************************
97 void CVegetableDensityPage::setVegetableToEdit(NL3D::CVegetable
*vegetable
)
99 _Vegetable
= vegetable
;
101 // If not NULL, init all controls.
106 StaticVegetableShape
.SetWindowText(nlUtf8ToTStr(_Vegetable
->ShapeName
));
108 // init Creation Distance.
110 // normalize creation distance for this editor.
111 _Vegetable
->DistType
= std::min( (uint
)_Vegetable
->DistType
, (uint
)(DistTypeCombo
.GetCount()-1) );
112 // set Creation Distance.
113 DistTypeCombo
.SetCurSel( _Vegetable
->DistType
);
117 _DensityDlg
->setNoiseValue(&_Vegetable
->Density
, _VegetableDlg
);
121 if(_Vegetable
->MaxDensity
== -1)
123 // Disable the checkBox and the slider.
124 MaxDensityCheckBox
.SetCheck(0);
126 _MaxDensityDlg
->setFloat(&dummy
, _VegetableDlg
);
127 _MaxDensityDlg
->EnableWindow(false);
128 // Init with a default value
129 _PrecMaxDensityValue
= NL_VEGETABLE_EDIT_DEFAULT_MAX_DENSITY
;
133 // Enable the checkBox and the slider
134 MaxDensityCheckBox
.SetCheck(1);
135 _MaxDensityDlg
->setFloat(&_Vegetable
->MaxDensity
, _VegetableDlg
);
136 _MaxDensityDlg
->EnableWindow(true);
141 NL3D::CVegetable::TAngleType angType
= _Vegetable
->getAngleType();
142 // disable 3 radio buttons.
143 ((CButton
*)GetDlgItem(IDC_RADIO_ANGLE_FLOOR
))->SetCheck(0);
144 ((CButton
*)GetDlgItem(IDC_RADIO_ANGLE_WALL
))->SetCheck(0);
145 ((CButton
*)GetDlgItem(IDC_RADIO_ANGLE_CEILING
))->SetCheck(0);
146 // enable only the one of interest.
149 case NL3D::CVegetable::AngleGround
:
150 ((CButton
*)GetDlgItem(IDC_RADIO_ANGLE_FLOOR
))->SetCheck(1);
151 enableAngleEdit(IDC_RADIO_ANGLE_FLOOR
);
153 case NL3D::CVegetable::AngleWall
:
154 ((CButton
*)GetDlgItem(IDC_RADIO_ANGLE_WALL
))->SetCheck(1);
155 enableAngleEdit(IDC_RADIO_ANGLE_WALL
);
157 case NL3D::CVegetable::AngleCeiling
:
158 ((CButton
*)GetDlgItem(IDC_RADIO_ANGLE_CEILING
))->SetCheck(1);
159 enableAngleEdit(IDC_RADIO_ANGLE_CEILING
);
162 // to avoid a strange bug if pos==0... (not correctly setuped the first time)
163 AngleMinSlider
.SetPos(10);
164 AngleMaxSlider
.SetPos(10);
165 // Init sliders / edit.
166 updateViewAngleMin();
167 updateViewAngleMax();
172 // ***************************************************************************
173 void CVegetableDensityPage::enableAngleEdit(uint radioId
)
176 AngleMinSlider
.EnableWindow(false);
177 AngleMinEdit
.EnableWindow(false);
178 AngleMaxSlider
.EnableWindow(false);
179 AngleMaxEdit
.EnableWindow(false);
180 // Then enable only what needed.
181 if(radioId
== IDC_RADIO_ANGLE_WALL
|| radioId
== IDC_RADIO_ANGLE_FLOOR
)
183 AngleMinSlider
.EnableWindow(true);
184 AngleMinEdit
.EnableWindow(true);
186 if(radioId
== IDC_RADIO_ANGLE_WALL
|| radioId
== IDC_RADIO_ANGLE_CEILING
)
188 AngleMaxSlider
.EnableWindow(true);
189 AngleMaxEdit
.EnableWindow(true);
194 // ***************************************************************************
195 void CVegetableDensityPage::updateViewAngleMin()
197 double angle
= _Vegetable
->getCosAngleMin();
198 NLMISC::clamp(angle
, -1, 1);
201 sint pos
= (sint
)(angle
/(NLMISC::Pi
/2) * NL_VEGETABLE_EDIT_ANGLE_SLIDER_SIZE
);
202 NLMISC::clamp(pos
, -NL_VEGETABLE_EDIT_ANGLE_SLIDER_SIZE
, NL_VEGETABLE_EDIT_ANGLE_SLIDER_SIZE
);
203 AngleMinSlider
.SetPos(pos
);
206 stmp
.Format(_T("%.2f"), (double)(angle
*180/NLMISC::Pi
));
207 AngleMinEdit
.SetWindowText(stmp
);
210 // ***************************************************************************
211 void CVegetableDensityPage::updateViewAngleMax()
213 double angle
= _Vegetable
->getCosAngleMax();
214 NLMISC::clamp(angle
, -1, 1);
217 sint pos
= (sint
)(angle
/(NLMISC::Pi
/2) * NL_VEGETABLE_EDIT_ANGLE_SLIDER_SIZE
);
218 NLMISC::clamp(pos
, -NL_VEGETABLE_EDIT_ANGLE_SLIDER_SIZE
, NL_VEGETABLE_EDIT_ANGLE_SLIDER_SIZE
);
219 AngleMaxSlider
.SetPos(pos
);
222 stmp
.Format(_T("%.2f"), (double)(angle
* 180 / NLMISC::Pi
));
223 AngleMaxEdit
.SetWindowText(stmp
);
227 // ***************************************************************************
228 void CVegetableDensityPage::updateAngleMinFromEditText()
230 // get angles edited.
232 AngleMinEdit
.GetWindowText(stmp
, 256);
234 NLMISC::fromString(NLMISC::tStrToUtf8(stmp
), angleMin
);
235 NLMISC::clamp(angleMin
, -90, 90);
236 // make a sinus, because 90 => 1, and -90 =>-1
237 float cosAngleMin
= (float)sin(angleMin
*NLMISC::Pi
/180.f
);
240 if(_Vegetable
->getAngleType()== NL3D::CVegetable::AngleWall
)
241 _Vegetable
->setAngleWall(cosAngleMin
, _Vegetable
->getCosAngleMax());
242 else if(_Vegetable
->getAngleType()== NL3D::CVegetable::AngleGround
)
243 _Vegetable
->setAngleGround(cosAngleMin
);
246 updateViewAngleMin();
249 _VegetableDlg
->refreshVegetableDisplay();
251 // ***************************************************************************
252 void CVegetableDensityPage::updateAngleMaxFromEditText()
254 // get angles edited.
256 AngleMaxEdit
.GetWindowText(stmp
, 256);
258 NLMISC::fromString(NLMISC::tStrToUtf8(stmp
), angleMax
);
259 NLMISC::clamp(angleMax
, -90, 90);
260 // make a sinus, because 90 => 1, and -90 =>-1
261 float cosAngleMax
= (float)sin(angleMax
*NLMISC::Pi
/180.f
);
264 if(_Vegetable
->getAngleType()== NL3D::CVegetable::AngleWall
)
265 _Vegetable
->setAngleWall(_Vegetable
->getCosAngleMin(), cosAngleMax
);
266 else if(_Vegetable
->getAngleType()== NL3D::CVegetable::AngleCeiling
)
267 _Vegetable
->setAngleCeiling(cosAngleMax
);
270 updateViewAngleMax();
273 _VegetableDlg
->refreshVegetableDisplay();
278 // ***************************************************************************
279 // ***************************************************************************
280 // CVegetableDensityPage message handlers
281 // ***************************************************************************
282 // ***************************************************************************
286 // ***************************************************************************
287 BOOL
CVegetableDensityPage::OnInitDialog()
289 CPropertyPage::OnInitDialog();
294 // Position of the density DlgBox relative to CVegetableDensityPage.
296 // y relative to MaxDensity Group
297 uint yMaxDensity
= 45;
298 // get y of MaxDensityStaticText relative to CVegetableDensityPage.
301 GetWindowRect(&rectParent
);
302 MaxDensityStaticText
.GetWindowRect(&rect
);
304 yMaxDensity
+= rect
.top
- rectParent
.top
;
307 // Init Density Dialog.
308 _DensityDlg
= new CVegetableNoiseValueDlg (std::string("Density"));
309 _DensityDlg
->setDefaultRangeAbs(NL_VEGETABLE_DENSITY_ABS_RANGE_MIN
, NL_VEGETABLE_DENSITY_ABS_RANGE_MAX
);
310 _DensityDlg
->setDefaultRangeRand(NL_VEGETABLE_DENSITY_RAND_RANGE_MIN
, NL_VEGETABLE_DENSITY_RAND_RANGE_MAX
);
311 _DensityDlg
->setDefaultRangeFreq(NL_VEGETABLE_FREQ_RANGE_MIN
, NL_VEGETABLE_FREQ_RANGE_MAX
);
312 _DensityDlg
->init(x
, yDensity
, this);
315 // Init MaxDensity Dialog.
316 _MaxDensityDlg
= new CDirectEditableRangeFloat (std::string("VEGET_MAX_DENSITY"), 0, NL_VEGETABLE_EDIT_DEFAULT_MAX_DENSITY
,
318 _MaxDensityDlg
->enableLowerBound(0, false);
319 _MaxDensityDlg
->init(x
+10, yMaxDensity
, this);
322 // Init angle sliders.
323 AngleMinSlider
.SetRange(-NL_VEGETABLE_EDIT_ANGLE_SLIDER_SIZE
, NL_VEGETABLE_EDIT_ANGLE_SLIDER_SIZE
);
324 AngleMaxSlider
.SetRange(-NL_VEGETABLE_EDIT_ANGLE_SLIDER_SIZE
, NL_VEGETABLE_EDIT_ANGLE_SLIDER_SIZE
);
328 StaticVegetableShape
.SetWindowText(_T(""));
331 return TRUE
; // return TRUE unless you set the focus to a control
332 // EXCEPTION: OCX Property Pages should return FALSE
335 // ***************************************************************************
336 void CVegetableDensityPage::OnCheckMaxDensity()
338 if(MaxDensityCheckBox
.GetCheck() == 1)
340 // check, restore maxDensity
341 _Vegetable
->MaxDensity
= _PrecMaxDensityValue
;
343 _MaxDensityDlg
->setFloat(&_Vegetable
->MaxDensity
, _VegetableDlg
);
344 _MaxDensityDlg
->EnableWindow(true);
348 // uncheck, bkup maxDenstiy
349 _PrecMaxDensityValue
= _Vegetable
->MaxDensity
;
352 _MaxDensityDlg
->setFloat(&dummy
, _VegetableDlg
);
353 _MaxDensityDlg
->EnableWindow(false);
354 // and setup vegetable (disable MaxDensity).
355 _Vegetable
->MaxDensity
= -1;
359 _VegetableDlg
->refreshVegetableDisplay();
362 // ***************************************************************************
363 void CVegetableDensityPage::OnSelchangeComboDistType()
365 // Get the DistType, and just copy to vegetable.
366 _Vegetable
->DistType
= DistTypeCombo
.GetCurSel();
368 // Since used to display name in selection listBox, must update the name
369 _VegetableDlg
->updateCurSelVegetableName();
372 _VegetableDlg
->refreshVegetableDisplay();
375 // ***************************************************************************
376 void CVegetableDensityPage::OnRadioAngleCeiling()
378 // Enable just the AngleMax slider.
379 enableAngleEdit(IDC_RADIO_ANGLE_CEILING
);
382 _Vegetable
->setAngleCeiling(0);
385 updateViewAngleMax();
388 _VegetableDlg
->refreshVegetableDisplay();
391 // ***************************************************************************
392 void CVegetableDensityPage::OnRadioAngleFloor()
394 // Enable just the AngleMin slider.
395 enableAngleEdit(IDC_RADIO_ANGLE_FLOOR
);
398 _Vegetable
->setAngleGround(0);
401 updateViewAngleMin();
404 _VegetableDlg
->refreshVegetableDisplay();
407 // ***************************************************************************
408 void CVegetableDensityPage::OnRadioAngleWall()
410 // Enable both sliders.
411 enableAngleEdit(IDC_RADIO_ANGLE_WALL
);
414 _Vegetable
->setAngleWall(-1, 1);
417 updateViewAngleMin();
418 updateViewAngleMax();
421 _VegetableDlg
->refreshVegetableDisplay();
424 // ***************************************************************************
425 void CVegetableDensityPage::OnReleasedcaptureSliderAngleMax(NMHDR
* pNMHDR
, LRESULT
* pResult
)
427 float angle
= 90 * (float)AngleMaxSlider
.GetPos() / (float)NL_VEGETABLE_EDIT_ANGLE_SLIDER_SIZE
;
428 NLMISC::clamp(angle
, -90, 90);
429 // make a sinus, because 90 => 1, and -90 =>-1
430 float cosAngleMax
= (float)sin(angle
*NLMISC::Pi
/180.f
);
433 if(_Vegetable
->getAngleType()== NL3D::CVegetable::AngleWall
)
434 _Vegetable
->setAngleWall(_Vegetable
->getCosAngleMin(), cosAngleMax
);
436 _Vegetable
->setAngleCeiling(cosAngleMax
);
439 updateViewAngleMax();
442 _VegetableDlg
->refreshVegetableDisplay();
447 // ***************************************************************************
448 void CVegetableDensityPage::OnReleasedcaptureSliderAngleMin(NMHDR
* pNMHDR
, LRESULT
* pResult
)
450 float angle
= 90 * (float)AngleMinSlider
.GetPos() / (float)NL_VEGETABLE_EDIT_ANGLE_SLIDER_SIZE
;
451 NLMISC::clamp(angle
, -90, 90);
452 // make a sinus, because 90 => 1, and -90 =>-1
453 float cosAngleMin
= (float)sin(angle
*NLMISC::Pi
/180.f
);
456 if(_Vegetable
->getAngleType()== NL3D::CVegetable::AngleWall
)
457 _Vegetable
->setAngleWall(cosAngleMin
, _Vegetable
->getCosAngleMax());
459 _Vegetable
->setAngleGround(cosAngleMin
);
462 updateViewAngleMin();
465 _VegetableDlg
->refreshVegetableDisplay();
470 // ***************************************************************************
471 void CVegetableDensityPage::OnKillfocusEditAngleMin()
473 updateAngleMinFromEditText();
475 void CVegetableDensityPage::OnKillfocusEditAngleMax()
477 updateAngleMaxFromEditText();
480 static void concatEdit2Lines(CEdit
&edit
)
482 const uint lineLen
= 1000;
484 // retrieve the 2 lines.
485 TCHAR tmp0
[2*lineLen
];
487 n
= edit
.GetLine(0, tmp0
, lineLen
); tmp0
[n
]= 0;
488 n
= edit
.GetLine(1, tmp1
, lineLen
); tmp1
[n
]= 0;
489 // concat and update the CEdit.
490 edit
.SetWindowText(_tcscat(tmp0
, tmp1
));
493 void CVegetableDensityPage::OnChangeEditAngleMin()
495 // Trick to track "Enter" keypress: CEdit are multiline. If GetLineCount()>1, then
496 // user has press enter.
497 if(AngleMinEdit
.GetLineCount()>1)
499 // must ccat the 2 first lines.
500 concatEdit2Lines(AngleMinEdit
);
501 // the text (and so the lineCount) is reseted in this method.
502 updateAngleMinFromEditText();
505 void CVegetableDensityPage::OnChangeEditAngleMax()
507 // Trick to track "Enter" keypress: CEdit are multiline. If GetLineCount()>1, then
508 // user has press enter.
509 if(AngleMaxEdit
.GetLineCount()>1)
511 // must ccat the 2 first lines.
512 concatEdit2Lines(AngleMinEdit
);
513 // the text (and so the lineCount) is reseted in this method.
514 updateAngleMaxFromEditText();
520 // ***************************************************************************
521 void CVegetableDensityPage::OnButtonVegetableBrowse()
523 CFileDialog
fd(TRUE
, _T("veget"), _T("*.veget"), 0, NULL
, this) ;
524 fd
.m_ofn
.lpstrTitle
= _T("Open Vegetable Shape");
526 if (fd
.DoModal() == IDOK
)
529 std::string fileName
= NLMISC::tStrToUtf8(fd
.GetFileName());
531 // Add search path for the .veget
532 std::string path
= NLMISC::CFile::getPath(NLMISC::tStrToUtf8(fd
.GetPathName()));
533 NLMISC::CPath::addSearchPath (path
);
537 // verify the file can be opened.
538 NLMISC::CPath::lookup(fileName
);
540 // update shapeName and view
541 _Vegetable
->ShapeName
= fileName
;
542 StaticVegetableShape
.SetWindowText(fd
.GetFileName());
544 // update the name in the list-box
545 _VegetableDlg
->updateCurSelVegetableName();
548 _VegetableDlg
->refreshVegetableDisplay();
550 catch (const NLMISC::EPathNotFound
&ep
)
552 MessageBox(nlUtf8ToTStr(ep
.what()), _T("Can't open file"));