386 lines
13 KiB
C++
386 lines
13 KiB
C++
|
// 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);
|
||
|
}
|