// NeL - MMORPG Framework <http://dev.ryzom.com/projects/nel/> // Copyright (C) 2010 Winch Gate Property Limited // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU Affero General Public License as // published by the Free Software Foundation, either version 3 of the // License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU Affero General Public License for more details. // // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see <http://www.gnu.org/licenses/>. #include "std_afx.h" #include "object_viewer.h" #include "emitter_dlg.h" #include "direction_attr.h" #include "particle_tree_ctrl.h" #include "particle_dlg.h" #include "nel/3d/particle_system.h" ///////////////////////////////////////////////////////////////////////////// // CEmitterDlg dialog CEmitterDlg::CEmitterDlg(CParticleWorkspace::CNode *ownerNode, NL3D::CPSEmitter *emitter, CParticleDlg *particleDlg) : _Node(ownerNode), _Emitter(emitter), _PeriodDlg(NULL), _GenNbDlg(NULL), _StrenghtModulateDlg(NULL), _SpeedInheritanceFactorDlg(NULL), _ParticleDlg(particleDlg) { nlassert(_Emitter); nlassert(_ParticleDlg); //{{AFX_DATA_INIT(CEmitterDlg) m_ConsistentEmission = _Emitter->isConsistentEmissionEnabled(); m_BypassAutoLOD = FALSE; //}}AFX_DATA_INIT } CEmitterDlg::~CEmitterDlg() { _PeriodDlg->DestroyWindow(); _GenNbDlg->DestroyWindow(); _StrenghtModulateDlg->DestroyWindow(); _SpeedInheritanceFactorDlg->DestroyWindow(); _DelayedEmissionDlg->DestroyWindow(); _MaxEmissionCountDlg->DestroyWindow(); delete _PeriodDlg; delete _GenNbDlg; delete _StrenghtModulateDlg; delete _SpeedInheritanceFactorDlg; delete _DelayedEmissionDlg; delete _MaxEmissionCountDlg; } void CEmitterDlg::init(CWnd* pParent) { Create(IDD_EMITTER_DIALOG, pParent); // fill the emitted type combo box with all the types of located initEmittedType(); m_EmissionTypeCtrl.SetCurSel((int) _Emitter->getEmissionType() ); ShowWindow(SW_SHOW); UpdateData(FALSE); } void CEmitterDlg::initEmittedType() { m_EmittedTypeCtrl.ResetContent(); NL3D::CParticleSystem *ps = _Emitter->getOwner()->getOwner(); uint nbLocated = ps->getNbProcess(); m_EmittedTypeCtrl.InitStorage(nbLocated, 16); for (uint k = 0; k < nbLocated; ++k) { NL3D::CPSLocated *loc = dynamic_cast<NL3D::CPSLocated *>(ps->getProcess(k)); if (loc) // is this a located { m_EmittedTypeCtrl.AddString(loc->getName().c_str()); _LocatedList.push_back(loc); if (loc == _Emitter->getEmittedType()) { m_EmittedTypeCtrl.SetCurSel(k); } } } } void CEmitterDlg::DoDataExchange(CDataExchange* pDX) { CDialog::DoDataExchange(pDX); //{{AFX_DATA_MAP(CEmitterDlg) DDX_Control(pDX, IDC_DIRECTION_MODE, m_DirectionModeCtrl); DDX_Control(pDX, IDC_TYPE_OF_EMISSION, m_EmissionTypeCtrl); DDX_Control(pDX, IDC_EMITTED_TYPE, m_EmittedTypeCtrl); DDX_Check(pDX, IDC_CONSISTENT_EMISSION, m_ConsistentEmission); DDX_Check(pDX, IDC_BYPASS_AUTOLOD, m_BypassAutoLOD); //}}AFX_DATA_MAP } BEGIN_MESSAGE_MAP(CEmitterDlg, CDialog) //{{AFX_MSG_MAP(CEmitterDlg) ON_CBN_SELCHANGE(IDC_EMITTED_TYPE, OnSelchangeEmittedType) ON_CBN_SELCHANGE(IDC_TYPE_OF_EMISSION, OnSelchangeTypeOfEmission) ON_BN_CLICKED(IDC_CONSISTENT_EMISSION, OnConsistentEmission) ON_BN_CLICKED(IDC_BYPASS_AUTOLOD, OnBypassAutoLOD) ON_CBN_SELCHANGE(IDC_DIRECTION_MODE, OnSelchangeDirectionMode) //}}AFX_MSG_MAP END_MESSAGE_MAP() ///////////////////////////////////////////////////////////////////////////// // CEmitterDlg message handlers void CEmitterDlg::OnSelchangeEmittedType() { UpdateData(); uint k = m_EmittedTypeCtrl.GetCurSel(); if (!_Emitter->setEmittedType(_LocatedList[k])) { if (_Emitter->getOwner()->getOwner()->getBehaviourType() == NL3D::CParticleSystem::SpellFX || _Emitter->getOwner()->getOwner()->getBypassMaxNumIntegrationSteps()) { MessageBox("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.", "Error", MB_ICONEXCLAMATION); } else { MessageBox("Loops with emitters are forbidden.", "Error", MB_ICONEXCLAMATION); } initEmittedType(); } _ParticleDlg->StartStopDlg->resetAutoCount(_Node); updateModifiedFlag(); } void CEmitterDlg::OnSelchangeTypeOfEmission() { UpdateData(); if (!_Emitter->setEmissionType((NL3D::CPSEmitter::TEmissionType) m_EmissionTypeCtrl.GetCurSel())) { CString mess; mess.LoadString(IDS_PS_NO_FINITE_DURATION); CString errorStr; errorStr.LoadString(IDS_ERROR); MessageBox((LPCTSTR) mess, (LPCTSTR) errorStr, MB_ICONEXCLAMATION); m_EmissionTypeCtrl.SetCurSel((int) _Emitter->getEmissionType()); } updatePeriodDlg(); _ParticleDlg->StartStopDlg->resetAutoCount(_Node); updateModifiedFlag(); } void CEmitterDlg::updatePeriodDlg(void) { BOOL bEnable = _Emitter->getEmissionType() == NL3D::CPSEmitter::regular; _PeriodDlg->EnableWindow(bEnable); _DelayedEmissionDlg->EnableWindow(bEnable); _MaxEmissionCountDlg->EnableWindow(bEnable); } BOOL CEmitterDlg::OnInitDialog() { CDialog::OnInitDialog(); RECT r; GetDlgItem(IDC_SPEED_INHERITANCE_FACTOR_FRAME)->GetWindowRect(&r); ScreenToClient(&r); _SpeedInheritanceFactorDlg = new CEditableRangeFloat("SPEED_INHERITANCE_FACTOR", _Node, -1.f, 1.f); _SpeedInheritanceFactorWrapper.E = _Emitter; _SpeedInheritanceFactorDlg->setWrapper(&_SpeedInheritanceFactorWrapper); _SpeedInheritanceFactorDlg->init(r.left, r.top, this); GetDlgItem(IDC_DELAYED_EMISSION_FRAME)->GetWindowRect(&r); ScreenToClient(&r); _DelayedEmissionDlg = new CEditableRangeFloat("DELAYED_EMISSION", _Node, 0.f, 10.f); _DelayedEmissionDlg->enableLowerBound(0.f, false); _DelayedEmissionWrapper.E = _Emitter; _DelayedEmissionWrapper.Node = _Node; _DelayedEmissionWrapper.SSPS = _ParticleDlg->StartStopDlg; _DelayedEmissionDlg->setWrapper(&_DelayedEmissionWrapper); _DelayedEmissionDlg->init(r.left, r.top, this); GetDlgItem(IDC_MAX_EMISSION_COUNT_FRAME)->GetWindowRect(&r); ScreenToClient(&r); _MaxEmissionCountDlg = new CEditableRangeUInt("MAX_EMISSION_COUNT", _Node, 0, 100); _MaxEmissionCountDlg->enableUpperBound(256, false); _MaxEmissionCountWrapper.E = _Emitter; _MaxEmissionCountWrapper.Node = _Node; _MaxEmissionCountWrapper.SSPS = _ParticleDlg->StartStopDlg; _MaxEmissionCountWrapper.HWnd = (HWND) (*this); _MaxEmissionCountDlg->setWrapper(&_MaxEmissionCountWrapper); _MaxEmissionCountDlg->init(r.left, r.top, this); _MaxEmissionCountWrapper.MaxEmissionCountDlg = _MaxEmissionCountDlg; uint posX = 13; uint posY = r.bottom + 5; // setup the dialog for the period of emission edition _PeriodDlg = new CAttribDlgFloat("EMISSION_PERIOD", _Node, 0.f, 2.f); _PeriodWrapper.E = _Emitter; _PeriodWrapper.Node = _Node; _PeriodWrapper.SSPS = _ParticleDlg->StartStopDlg; _PeriodDlg->setWrapper(&_PeriodWrapper); _PeriodDlg->setSchemeWrapper(&_PeriodWrapper); HBITMAP bmh = LoadBitmap(::AfxGetInstanceHandle(), MAKEINTRESOURCE(IDB_EMISSION_PERIOD)); _PeriodDlg->init(bmh, posX, posY, this); posY += 120; // setup the dialog that helps tuning the number of particle being emitted at a time _GenNbDlg = new CAttribDlgUInt("EMISSION_GEN_NB", _Node, 1, 11); _GenNbWrapper.E = _Emitter; _GenNbWrapper.Node = _Node; _GenNbWrapper.SSPS = _ParticleDlg->StartStopDlg; _GenNbDlg->setWrapper(&_GenNbWrapper); _GenNbDlg->setSchemeWrapper(&_GenNbWrapper); bmh = LoadBitmap(::AfxGetInstanceHandle(), MAKEINTRESOURCE(IDB_EMISSION_QUANTITY)); _GenNbDlg->init(bmh, posX, posY, this); posY += 120; if (dynamic_cast<NL3D::CPSModulatedEmitter *>(_Emitter)) { _StrenghtModulateDlg = new CAttribDlgFloat("EMISSION_GEN_NB", _Node, 1, 11); _ModulatedStrenghtWrapper.E = dynamic_cast<NL3D::CPSModulatedEmitter *>(_Emitter); _StrenghtModulateDlg->setWrapper(&_ModulatedStrenghtWrapper); _StrenghtModulateDlg->setSchemeWrapper(&_ModulatedStrenghtWrapper); bmh = LoadBitmap(::AfxGetInstanceHandle(), MAKEINTRESOURCE(IDB_MODULATE_STRENGHT)); _StrenghtModulateDlg->init(bmh, posX, posY, this); posY += 120; } // deals with emitters that have a direction if (dynamic_cast<NL3D::CPSDirection *>(_Emitter)) { CDirectionAttr *da = new CDirectionAttr(std::string("DIRECTION")); pushWnd(da); _DirectionWrapper.E = dynamic_cast<NL3D::CPSDirection *>(_Emitter); da->setWrapper(&_DirectionWrapper); da->setDirectionWrapper(dynamic_cast<NL3D::CPSDirection *>(_Emitter)); da->init(posX, posY, this); da->GetClientRect(&r); posY += r.bottom; } // radius for conic emitter if (dynamic_cast<NL3D::CPSEmitterConic *>(_Emitter)) { CEditableRangeFloat *ecr = new CEditableRangeFloat(std::string("CONIC EMITTER RADIUS"), _Node, 0.1f, 2.1f); pushWnd(ecr); _ConicEmitterRadiusWrapper.E = dynamic_cast<NL3D::CPSEmitterConic *>(_Emitter); ecr->setWrapper(&_ConicEmitterRadiusWrapper); ecr->init(posX + 80, posY, this); CStatic *s = new CStatic; pushWnd(s); s->Create("Radius :", SS_LEFT, CRect(posX, posY + 10 , posX + 70, posY + 32), this); s->SetFont(CFont::FromHandle((HFONT) GetStockObject(DEFAULT_GUI_FONT))); s->ShowWindow(SW_SHOW); ecr->GetClientRect(&r); posY += r.bottom; } if (_Emitter->isSpeedBasisEmissionEnabled()) { m_DirectionModeCtrl.SetCurSel((int)AlignOnEmitterDirection); } else if (!_Emitter->isUserMatrixModeForEmissionDirectionEnabled()) { m_DirectionModeCtrl.SetCurSel((int)Default); } else if (_Emitter->getUserMatrixModeForEmissionDirection() == NL3D::PSFXWorldMatrix) { m_DirectionModeCtrl.SetCurSel((int)LocalToSystem); } else if (_Emitter->getUserMatrixModeForEmissionDirection() == NL3D::PSIdentityMatrix) { m_DirectionModeCtrl.SetCurSel((int)InWorld); } else if (_Emitter->getUserMatrixModeForEmissionDirection() == NL3D::PSUserMatrix) { m_DirectionModeCtrl.SetCurSel((int)LocalToFatherSkeleton); } else { nlassert(0); } updatePeriodDlg(); // bypass auto LOD nlassert(_Emitter->getOwner() && _Emitter->getOwner()->getOwner()); NL3D::CParticleSystem &ps = *_Emitter->getOwner()->getOwner(); CButton *button = (CButton *) GetDlgItem(IDC_BYPASS_AUTOLOD); if (ps.isAutoLODEnabled() && !ps.isSharingEnabled()) { button->EnableWindow(TRUE); m_BypassAutoLOD = _Emitter->getBypassAutoLOD(); } else { button->EnableWindow(FALSE); } return TRUE; // return TRUE unless you set the focus to a control // EXCEPTION: OCX Property Pages should return FALSE } void CEmitterDlg::OnConsistentEmission() { UpdateData(); _Emitter->enableConsistenEmission(m_ConsistentEmission != 0 ? true : false /* VC6 warning */); updateModifiedFlag(); UpdateData(TRUE); } void CEmitterDlg::OnBypassAutoLOD() { UpdateData(); _Emitter->setBypassAutoLOD(m_BypassAutoLOD ? true : false); UpdateData(TRUE); updateModifiedFlag(); } void CEmitterDlg::CMaxEmissionCountWrapper::set(const uint32 &count) { if (!E->setMaxEmissionCount((uint8) count)) { CString mess; mess.LoadString(IDS_PS_NO_FINITE_DURATION); CString errorStr; errorStr.LoadString(IDS_ERROR); ::MessageBox(HWnd, (LPCTSTR) mess, (LPCTSTR) errorStr, MB_ICONEXCLAMATION); MaxEmissionCountDlg->updateValueFromReader(); } SSPS->resetAutoCount(Node); } void CEmitterDlg::OnSelchangeDirectionMode() { UpdateData(); nlassert(_Emitter); switch(m_DirectionModeCtrl.GetCurSel()) { case Default: _Emitter->enableSpeedBasisEmission(false); _Emitter->enableUserMatrixModeForEmissionDirection(false); break; case AlignOnEmitterDirection: _Emitter->enableSpeedBasisEmission(true); _Emitter->enableUserMatrixModeForEmissionDirection(false); break; case InWorld: _Emitter->enableSpeedBasisEmission(false); _Emitter->enableUserMatrixModeForEmissionDirection(true); _Emitter->setUserMatrixModeForEmissionDirection(NL3D::PSIdentityMatrix); break; case LocalToSystem: _Emitter->enableSpeedBasisEmission(false); _Emitter->enableUserMatrixModeForEmissionDirection(true); _Emitter->setUserMatrixModeForEmissionDirection(NL3D::PSFXWorldMatrix); break; case LocalToFatherSkeleton: _Emitter->enableSpeedBasisEmission(false); _Emitter->enableUserMatrixModeForEmissionDirection(true); _Emitter->setUserMatrixModeForEmissionDirection(NL3D::PSUserMatrix); break; } updateModifiedFlag(); UpdateData(FALSE); }