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/>.
21 #include "object_viewer.h"
22 #include "emitter_dlg.h"
23 #include "direction_attr.h"
24 #include "particle_tree_ctrl.h"
25 #include "particle_dlg.h"
27 #include "nel/3d/particle_system.h"
31 /////////////////////////////////////////////////////////////////////////////
35 CEmitterDlg::CEmitterDlg(CParticleWorkspace::CNode
*ownerNode
, NL3D::CPSEmitter
*emitter
, CParticleDlg
*particleDlg
)
40 _StrenghtModulateDlg(NULL
),
41 _SpeedInheritanceFactorDlg(NULL
),
42 _ParticleDlg(particleDlg
)
45 nlassert(_ParticleDlg
);
46 //{{AFX_DATA_INIT(CEmitterDlg)
47 m_ConsistentEmission
= _Emitter
->isConsistentEmissionEnabled();
48 m_BypassAutoLOD
= FALSE
;
52 CEmitterDlg::~CEmitterDlg()
54 _PeriodDlg
->DestroyWindow();
55 _GenNbDlg
->DestroyWindow();
56 _StrenghtModulateDlg
->DestroyWindow();
57 _SpeedInheritanceFactorDlg
->DestroyWindow();
58 _DelayedEmissionDlg
->DestroyWindow();
59 _MaxEmissionCountDlg
->DestroyWindow();
63 delete _StrenghtModulateDlg
;
64 delete _SpeedInheritanceFactorDlg
;
65 delete _DelayedEmissionDlg
;
66 delete _MaxEmissionCountDlg
;
70 void CEmitterDlg::init(CWnd
* pParent
)
72 Create(IDD_EMITTER_DIALOG
, pParent
);
73 // fill the emitted type combo box with all the types of located
75 m_EmissionTypeCtrl
.SetCurSel((int) _Emitter
->getEmissionType() );
80 void CEmitterDlg::initEmittedType()
82 m_EmittedTypeCtrl
.ResetContent();
83 NL3D::CParticleSystem
*ps
= _Emitter
->getOwner()->getOwner();
84 uint nbLocated
= ps
->getNbProcess();
85 m_EmittedTypeCtrl
.InitStorage(nbLocated
, 16);
86 for (uint k
= 0; k
< nbLocated
; ++k
)
88 NL3D::CPSLocated
*loc
= dynamic_cast<NL3D::CPSLocated
*>(ps
->getProcess(k
));
89 if (loc
) // is this a located
91 m_EmittedTypeCtrl
.AddString(nlUtf8ToTStr(loc
->getName()));
92 _LocatedList
.push_back(loc
);
93 if (loc
== _Emitter
->getEmittedType())
95 m_EmittedTypeCtrl
.SetCurSel(k
);
101 void CEmitterDlg::DoDataExchange(CDataExchange
* pDX
)
103 CDialog::DoDataExchange(pDX
);
104 //{{AFX_DATA_MAP(CEmitterDlg)
105 DDX_Control(pDX
, IDC_DIRECTION_MODE
, m_DirectionModeCtrl
);
106 DDX_Control(pDX
, IDC_TYPE_OF_EMISSION
, m_EmissionTypeCtrl
);
107 DDX_Control(pDX
, IDC_EMITTED_TYPE
, m_EmittedTypeCtrl
);
108 DDX_Check(pDX
, IDC_CONSISTENT_EMISSION
, m_ConsistentEmission
);
109 DDX_Check(pDX
, IDC_BYPASS_AUTOLOD
, m_BypassAutoLOD
);
114 BEGIN_MESSAGE_MAP(CEmitterDlg
, CDialog
)
115 //{{AFX_MSG_MAP(CEmitterDlg)
116 ON_CBN_SELCHANGE(IDC_EMITTED_TYPE
, OnSelchangeEmittedType
)
117 ON_CBN_SELCHANGE(IDC_TYPE_OF_EMISSION
, OnSelchangeTypeOfEmission
)
118 ON_BN_CLICKED(IDC_CONSISTENT_EMISSION
, OnConsistentEmission
)
119 ON_BN_CLICKED(IDC_BYPASS_AUTOLOD
, OnBypassAutoLOD
)
120 ON_CBN_SELCHANGE(IDC_DIRECTION_MODE
, OnSelchangeDirectionMode
)
124 /////////////////////////////////////////////////////////////////////////////
125 // CEmitterDlg message handlers
127 void CEmitterDlg::OnSelchangeEmittedType()
130 uint k
= m_EmittedTypeCtrl
.GetCurSel();
131 if (!_Emitter
->setEmittedType(_LocatedList
[k
]))
133 if (_Emitter
->getOwner()->getOwner()->getBehaviourType() == NL3D::CParticleSystem::SpellFX
|| _Emitter
->getOwner()->getOwner()->getBypassMaxNumIntegrationSteps())
135 MessageBox(_T("Can't perform operation : the system is flagged with 'No max nb steps' or uses the preset 'Spell FX', and thus, should have a finite duration. This operation create a loop in the system, and so is forbidden."), _T("Error"), MB_ICONEXCLAMATION
);
139 MessageBox(_T("Loops with emitters are forbidden."), _T("Error"), MB_ICONEXCLAMATION
);
143 _ParticleDlg
->StartStopDlg
->resetAutoCount(_Node
);
144 updateModifiedFlag();
147 void CEmitterDlg::OnSelchangeTypeOfEmission()
150 if (!_Emitter
->setEmissionType((NL3D::CPSEmitter::TEmissionType
) m_EmissionTypeCtrl
.GetCurSel()))
153 mess
.LoadString(IDS_PS_NO_FINITE_DURATION
);
155 errorStr
.LoadString(IDS_ERROR
);
156 MessageBox((LPCTSTR
) mess
, (LPCTSTR
) errorStr
, MB_ICONEXCLAMATION
);
157 m_EmissionTypeCtrl
.SetCurSel((int) _Emitter
->getEmissionType());
161 _ParticleDlg
->StartStopDlg
->resetAutoCount(_Node
);
162 updateModifiedFlag();
166 void CEmitterDlg::updatePeriodDlg(void)
168 BOOL bEnable
= _Emitter
->getEmissionType() == NL3D::CPSEmitter::regular
;
169 _PeriodDlg
->EnableWindow(bEnable
);
170 _DelayedEmissionDlg
->EnableWindow(bEnable
);
171 _MaxEmissionCountDlg
->EnableWindow(bEnable
);
174 BOOL
CEmitterDlg::OnInitDialog()
176 CDialog::OnInitDialog();
180 GetDlgItem(IDC_SPEED_INHERITANCE_FACTOR_FRAME
)->GetWindowRect(&r
);
182 _SpeedInheritanceFactorDlg
= new CEditableRangeFloat("SPEED_INHERITANCE_FACTOR", _Node
, -1.f
, 1.f
);
183 _SpeedInheritanceFactorWrapper
.E
= _Emitter
;
184 _SpeedInheritanceFactorDlg
->setWrapper(&_SpeedInheritanceFactorWrapper
);
185 _SpeedInheritanceFactorDlg
->init(r
.left
, r
.top
, this);
187 GetDlgItem(IDC_DELAYED_EMISSION_FRAME
)->GetWindowRect(&r
);
189 _DelayedEmissionDlg
= new CEditableRangeFloat("DELAYED_EMISSION", _Node
, 0.f
, 10.f
);
190 _DelayedEmissionDlg
->enableLowerBound(0.f
, false);
191 _DelayedEmissionWrapper
.E
= _Emitter
;
192 _DelayedEmissionWrapper
.Node
= _Node
;
193 _DelayedEmissionWrapper
.SSPS
= _ParticleDlg
->StartStopDlg
;
194 _DelayedEmissionDlg
->setWrapper(&_DelayedEmissionWrapper
);
195 _DelayedEmissionDlg
->init(r
.left
, r
.top
, this);
197 GetDlgItem(IDC_MAX_EMISSION_COUNT_FRAME
)->GetWindowRect(&r
);
199 _MaxEmissionCountDlg
= new CEditableRangeUInt("MAX_EMISSION_COUNT", _Node
, 0, 100);
200 _MaxEmissionCountDlg
->enableUpperBound(256, false);
201 _MaxEmissionCountWrapper
.E
= _Emitter
;
202 _MaxEmissionCountWrapper
.Node
= _Node
;
203 _MaxEmissionCountWrapper
.SSPS
= _ParticleDlg
->StartStopDlg
;
204 _MaxEmissionCountWrapper
.HWnd
= (HWND
) (*this);
205 _MaxEmissionCountDlg
->setWrapper(&_MaxEmissionCountWrapper
);
206 _MaxEmissionCountDlg
->init(r
.left
, r
.top
, this);
207 _MaxEmissionCountWrapper
.MaxEmissionCountDlg
= _MaxEmissionCountDlg
;
212 uint posY
= r
.bottom
+ 5;
214 // setup the dialog for the period of emission edition
216 _PeriodDlg
= new CAttribDlgFloat("EMISSION_PERIOD", _Node
, 0.f
, 2.f
);
217 _PeriodWrapper
.E
= _Emitter
;
218 _PeriodWrapper
.Node
= _Node
;
219 _PeriodWrapper
.SSPS
= _ParticleDlg
->StartStopDlg
;
220 _PeriodDlg
->setWrapper(&_PeriodWrapper
);
221 _PeriodDlg
->setSchemeWrapper(&_PeriodWrapper
);
222 HBITMAP bmh
= LoadBitmap(::AfxGetInstanceHandle(), MAKEINTRESOURCE(IDB_EMISSION_PERIOD
));
223 _PeriodDlg
->init(bmh
, posX
, posY
, this);
226 // setup the dialog that helps tuning the number of particle being emitted at a time
228 _GenNbDlg
= new CAttribDlgUInt("EMISSION_GEN_NB", _Node
, 1, 11);
229 _GenNbWrapper
.E
= _Emitter
;
230 _GenNbWrapper
.Node
= _Node
;
231 _GenNbWrapper
.SSPS
= _ParticleDlg
->StartStopDlg
;
232 _GenNbDlg
->setWrapper(&_GenNbWrapper
);
233 _GenNbDlg
->setSchemeWrapper(&_GenNbWrapper
);
234 bmh
= LoadBitmap(::AfxGetInstanceHandle(), MAKEINTRESOURCE(IDB_EMISSION_QUANTITY
));
235 _GenNbDlg
->init(bmh
, posX
, posY
, this);
238 if (dynamic_cast<NL3D::CPSModulatedEmitter
*>(_Emitter
))
240 _StrenghtModulateDlg
= new CAttribDlgFloat("EMISSION_GEN_NB", _Node
, 1, 11);
241 _ModulatedStrenghtWrapper
.E
= dynamic_cast<NL3D::CPSModulatedEmitter
*>(_Emitter
);
242 _StrenghtModulateDlg
->setWrapper(&_ModulatedStrenghtWrapper
);
243 _StrenghtModulateDlg
->setSchemeWrapper(&_ModulatedStrenghtWrapper
);
244 bmh
= LoadBitmap(::AfxGetInstanceHandle(), MAKEINTRESOURCE(IDB_MODULATE_STRENGHT
));
245 _StrenghtModulateDlg
->init(bmh
, posX
, posY
, this);
249 // deals with emitters that have a direction
250 if (dynamic_cast<NL3D::CPSDirection
*>(_Emitter
))
252 CDirectionAttr
*da
= new CDirectionAttr(std::string("DIRECTION"));
254 _DirectionWrapper
.E
= dynamic_cast<NL3D::CPSDirection
*>(_Emitter
);
255 da
->setWrapper(&_DirectionWrapper
);
256 da
->setDirectionWrapper(dynamic_cast<NL3D::CPSDirection
*>(_Emitter
));
257 da
->init(posX
, posY
, this);
258 da
->GetClientRect(&r
);
262 // radius for conic emitter
263 if (dynamic_cast<NL3D::CPSEmitterConic
*>(_Emitter
))
265 CEditableRangeFloat
*ecr
= new CEditableRangeFloat(std::string("CONIC EMITTER RADIUS"), _Node
, 0.1f
, 2.1f
);
267 _ConicEmitterRadiusWrapper
.E
= dynamic_cast<NL3D::CPSEmitterConic
*>(_Emitter
);
268 ecr
->setWrapper(&_ConicEmitterRadiusWrapper
);
269 ecr
->init(posX
+ 80, posY
, this);
271 CStatic
*s
= new CStatic
;
273 s
->Create(_T("Radius :"), SS_LEFT
, CRect(posX
, posY
+ 10 , posX
+ 70, posY
+ 32), this);
274 s
->SetFont(CFont::FromHandle((HFONT
) GetStockObject(DEFAULT_GUI_FONT
)));
275 s
->ShowWindow(SW_SHOW
);
277 ecr
->GetClientRect(&r
);
281 if (_Emitter
->isSpeedBasisEmissionEnabled())
283 m_DirectionModeCtrl
.SetCurSel((int)AlignOnEmitterDirection
);
285 else if (!_Emitter
->isUserMatrixModeForEmissionDirectionEnabled())
287 m_DirectionModeCtrl
.SetCurSel((int)Default
);
289 else if (_Emitter
->getUserMatrixModeForEmissionDirection() == NL3D::PSFXWorldMatrix
)
291 m_DirectionModeCtrl
.SetCurSel((int)LocalToSystem
);
293 else if (_Emitter
->getUserMatrixModeForEmissionDirection() == NL3D::PSIdentityMatrix
)
295 m_DirectionModeCtrl
.SetCurSel((int)InWorld
);
297 else if (_Emitter
->getUserMatrixModeForEmissionDirection() == NL3D::PSUserMatrix
)
299 m_DirectionModeCtrl
.SetCurSel((int)LocalToFatherSkeleton
);
309 nlassert(_Emitter
->getOwner() && _Emitter
->getOwner()->getOwner());
310 NL3D::CParticleSystem
&ps
= *_Emitter
->getOwner()->getOwner();
311 CButton
*button
= (CButton
*) GetDlgItem(IDC_BYPASS_AUTOLOD
);
312 if (ps
.isAutoLODEnabled() && !ps
.isSharingEnabled())
314 button
->EnableWindow(TRUE
);
315 m_BypassAutoLOD
= _Emitter
->getBypassAutoLOD();
319 button
->EnableWindow(FALSE
);
322 return TRUE
; // return TRUE unless you set the focus to a control
323 // EXCEPTION: OCX Property Pages should return FALSE
326 void CEmitterDlg::OnConsistentEmission()
329 _Emitter
->enableConsistenEmission(m_ConsistentEmission
!= 0 ? true : false /* VC6 warning */);
330 updateModifiedFlag();
334 void CEmitterDlg::OnBypassAutoLOD()
337 _Emitter
->setBypassAutoLOD(m_BypassAutoLOD
? true : false);
339 updateModifiedFlag();
342 void CEmitterDlg::CMaxEmissionCountWrapper::set(const uint32
&count
)
344 if (!E
->setMaxEmissionCount((uint8
) count
))
347 mess
.LoadString(IDS_PS_NO_FINITE_DURATION
);
349 errorStr
.LoadString(IDS_ERROR
);
350 ::MessageBox(HWnd
, (LPCTSTR
) mess
, (LPCTSTR
) errorStr
, MB_ICONEXCLAMATION
);
351 MaxEmissionCountDlg
->updateValueFromReader();
353 SSPS
->resetAutoCount(Node
);
356 void CEmitterDlg::OnSelchangeDirectionMode()
360 switch(m_DirectionModeCtrl
.GetCurSel())
363 _Emitter
->enableSpeedBasisEmission(false);
364 _Emitter
->enableUserMatrixModeForEmissionDirection(false);
366 case AlignOnEmitterDirection
:
367 _Emitter
->enableSpeedBasisEmission(true);
368 _Emitter
->enableUserMatrixModeForEmissionDirection(false);
371 _Emitter
->enableSpeedBasisEmission(false);
372 _Emitter
->enableUserMatrixModeForEmissionDirection(true);
373 _Emitter
->setUserMatrixModeForEmissionDirection(NL3D::PSIdentityMatrix
);
376 _Emitter
->enableSpeedBasisEmission(false);
377 _Emitter
->enableUserMatrixModeForEmissionDirection(true);
378 _Emitter
->setUserMatrixModeForEmissionDirection(NL3D::PSFXWorldMatrix
);
380 case LocalToFatherSkeleton
:
381 _Emitter
->enableSpeedBasisEmission(false);
382 _Emitter
->enableUserMatrixModeForEmissionDirection(true);
383 _Emitter
->setUserMatrixModeForEmissionDirection(NL3D::PSUserMatrix
);
386 updateModifiedFlag();