mirror of
https://port.numenaute.org/aleajactaest/khanat-opennel-code.git
synced 2024-12-27 03:10:54 +00:00
757 lines
24 KiB
C++
757 lines
24 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/>.
|
||
|
|
||
|
|
||
|
// particle_dlg.cpp : implementation file
|
||
|
//
|
||
|
|
||
|
#include "std_afx.h"
|
||
|
#include "object_viewer.h"
|
||
|
#include "particle_dlg.h"
|
||
|
#include "editable_range.h"
|
||
|
#include "located_properties.h"
|
||
|
#include "particle_system_edit.h"
|
||
|
#include "skippable_message_box.h"
|
||
|
#include "main_frame.h"
|
||
|
//
|
||
|
// TODO : remove these include when the test system will be removed
|
||
|
#include "nel/3d/particle_system.h"
|
||
|
#include "nel/3d/ps_force.h"
|
||
|
#include "nel/3d/ps_emitter.h"
|
||
|
#include "nel/3d/ps_particle.h"
|
||
|
#include "nel/3d/ps_util.h"
|
||
|
#include "nel/3d/ps_zone.h"
|
||
|
#include "nel/3d/ps_color.h"
|
||
|
#include "nel/3d/ps_float.h"
|
||
|
#include "nel/3d/ps_int.h"
|
||
|
#include "nel/3d/ps_plane_basis_maker.h"
|
||
|
#include "nel/3d/particle_system_model.h"
|
||
|
#include "nel/3d/particle_system_shape.h"
|
||
|
#include "nel/3d/texture_file.h"
|
||
|
#include "nel/3d/texture_grouped.h"
|
||
|
#include "nel/3d/nelu.h"
|
||
|
#include "nel/3d/font_manager.h"
|
||
|
#include "nel/3d/font_generator.h"
|
||
|
//
|
||
|
#include "nel/misc/file.h"
|
||
|
#include "start_stop_particle_system.h"
|
||
|
//
|
||
|
#include "save_options_dlg.h"
|
||
|
#include "create_file_dlg.h"
|
||
|
|
||
|
using namespace NL3D;
|
||
|
|
||
|
//**************************************************************************************************************************
|
||
|
CParticleDlg::CParticleDlg(class CObjectViewer* main, CWnd *pParent, CMainFrame* mainFrame, CAnimationDlg *animDLG)
|
||
|
: CDialog(CParticleDlg::IDD, pParent),
|
||
|
MainFrame(mainFrame),
|
||
|
CurrentRightPane(NULL),
|
||
|
_ActiveNode(NULL),
|
||
|
_ObjView(main),
|
||
|
_EmptyBBox(true),
|
||
|
_AutoUpdateBBox(false),
|
||
|
_PW(NULL)
|
||
|
|
||
|
{
|
||
|
//{{AFX_DATA_INIT(CParticleDlg)
|
||
|
// NOTE: the ClassWizard will add member initialization here
|
||
|
//}}AFX_DATA_INIT
|
||
|
nlverify (FontManager = main->getFontManager());
|
||
|
nlverify (FontGenerator = main->getFontGenerator());
|
||
|
NL3D::CParticleSystem::setSerializeIdentifierFlag(true); // serialize identifiers for edition
|
||
|
ParticleTreeCtrl = new CParticleTreeCtrl(this);
|
||
|
StartStopDlg = new CStartStopParticleSystem(this, animDLG);
|
||
|
/** register us, so that our 'go' method will be called
|
||
|
* this gives us a chance to display a bbox when needed
|
||
|
*/
|
||
|
_ObjView->registerMainLoopCallBack(this);
|
||
|
}
|
||
|
|
||
|
|
||
|
//**************************************************************************************************************************
|
||
|
BOOL CParticleDlg::Create( UINT nIDTemplate, CWnd* pParentWnd /*= NULL*/ )
|
||
|
{
|
||
|
if (!CDialog::Create(nIDTemplate, pParentWnd)) return FALSE;
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
//**************************************************************************************************************************
|
||
|
void CParticleDlg::moveElement(const NLMISC::CMatrix &mat)
|
||
|
{
|
||
|
ParticleTreeCtrl->moveElement(mat);
|
||
|
}
|
||
|
|
||
|
//**************************************************************************************************************************
|
||
|
NLMISC::CMatrix CParticleDlg::getElementMatrix(void) const
|
||
|
{
|
||
|
return ParticleTreeCtrl->getElementMatrix();
|
||
|
}
|
||
|
|
||
|
//**************************************************************************************************************************
|
||
|
CParticleDlg::~CParticleDlg()
|
||
|
{
|
||
|
_ObjView->removeMainLoopCallBack(this);
|
||
|
delete ParticleTreeCtrl;
|
||
|
delete CurrentRightPane;
|
||
|
delete StartStopDlg;
|
||
|
if (_PW) _PW->setModificationCallback(NULL);
|
||
|
delete _PW;
|
||
|
}
|
||
|
|
||
|
//**************************************************************************************************************************
|
||
|
void CParticleDlg::DoDataExchange(CDataExchange* pDX)
|
||
|
{
|
||
|
CDialog::DoDataExchange(pDX);
|
||
|
//{{AFX_DATA_MAP(CParticleDlg)
|
||
|
//}}AFX_DATA_MAP
|
||
|
}
|
||
|
|
||
|
|
||
|
BEGIN_MESSAGE_MAP(CParticleDlg, CDialog)
|
||
|
//{{AFX_MSG_MAP(CParticleDlg)
|
||
|
ON_WM_DESTROY()
|
||
|
ON_WM_SIZE()
|
||
|
ON_WM_SHOWWINDOW()
|
||
|
ON_WM_CHAR()
|
||
|
ON_COMMAND(IDM_CREATE_NEW_PS_WORKSPACE, OnCreateNewPsWorkspace)
|
||
|
ON_COMMAND(IDM_LOAD_PS_WORKSPACE, OnLoadPSWorkspace)
|
||
|
ON_COMMAND(IDM_SAVE_ALL_PS_WORKSPACE, OnSaveAllPsWorkspace)
|
||
|
ON_COMMAND(IDM_SAVE_PS_WORKSPACE, OnSavePsWorkspace)
|
||
|
ON_COMMAND(IDM_VIEW_PS_FILENAME, OnViewPsFilename)
|
||
|
//}}AFX_MSG_MAP
|
||
|
END_MESSAGE_MAP()
|
||
|
|
||
|
//**************************************************************************************************************************
|
||
|
void CParticleDlg::OnDestroy()
|
||
|
{
|
||
|
checkModifiedWorkSpace();
|
||
|
if (CurrentRightPane)
|
||
|
{
|
||
|
CurrentRightPane->DestroyWindow();
|
||
|
delete CurrentRightPane;
|
||
|
CurrentRightPane = NULL;
|
||
|
}
|
||
|
setRegisterWindowState (this, REGKEY_OBJ_PARTICLE_DLG);
|
||
|
CDialog::OnDestroy();
|
||
|
}
|
||
|
|
||
|
//**************************************************************************************************************************
|
||
|
BOOL CParticleDlg::OnInitDialog()
|
||
|
{
|
||
|
CDialog::OnInitDialog();
|
||
|
CRect r;
|
||
|
GetWindowRect(&r);
|
||
|
ParticleTreeCtrl->Create(WS_VISIBLE | WS_TABSTOP | WS_CHILD | WS_BORDER
|
||
|
| TVS_HASBUTTONS | TVS_LINESATROOT | TVS_HASLINES | TVS_SHOWSELALWAYS | TVS_EDITLABELS
|
||
|
| TVS_DISABLEDRAGDROP , r, this, 0x1005);
|
||
|
ParticleTreeCtrl->init();
|
||
|
ParticleTreeCtrl->ShowWindow(SW_SHOW);
|
||
|
StartStopDlg->Create(IDD_PARTICLE_SYSTEM_START_STOP, this);
|
||
|
// create menu bar that allow to create / load a particle workspace
|
||
|
CMenu menu;
|
||
|
menu.LoadMenu(MAKEINTRESOURCE(IDR_PARTICLE_DLG_MENU));
|
||
|
this->SetMenu(&menu);
|
||
|
menu.Detach();
|
||
|
updateMenu();
|
||
|
//
|
||
|
_StatusBar.Create(this);
|
||
|
UINT indicators = ID_PS_EDITOR_STATUS;
|
||
|
_StatusBar.SetIndicators(&indicators, 1);
|
||
|
_StatusBar.SetPaneInfo(0, ID_PS_EDITOR_STATUS, SBPS_NORMAL, computeStatusBarWidth());
|
||
|
RepositionBars(AFX_IDW_CONTROLBAR_FIRST, AFX_IDW_CONTROLBAR_LAST, ID_PS_EDITOR_STATUS);
|
||
|
return TRUE; // return TRUE unless you set the focus to a control
|
||
|
// EXCEPTION: OCX Property Pages should return FALSE
|
||
|
}
|
||
|
|
||
|
//**************************************************************************************************************************
|
||
|
void CParticleDlg::setStatusBarText(CString &str)
|
||
|
{
|
||
|
_StatusBar.SetPaneText(0, str, TRUE);
|
||
|
}
|
||
|
|
||
|
//**************************************************************************************************************************
|
||
|
void CParticleDlg::OnSize(UINT nType, int cx, int cy)
|
||
|
{
|
||
|
AFX_MANAGE_STATE(AfxGetStaticModuleState());
|
||
|
bool blocked = false;
|
||
|
if (ParticleTreeCtrl->m_hWnd && this->m_hWnd)
|
||
|
{
|
||
|
CRect r = getTreeRect(cx, cy);
|
||
|
ParticleTreeCtrl->MoveWindow(&r);
|
||
|
if (CurrentRightPane)
|
||
|
{
|
||
|
CurrentRightPane->MoveWindow(r.right + 10, r.top, r.right + CurrRightPaneWidth + 10, r.top + CurrRightPaneHeight);
|
||
|
}
|
||
|
CDialog::OnSize(nType, cx, cy);
|
||
|
if (IsWindow(_StatusBar))
|
||
|
{
|
||
|
_StatusBar.SetPaneInfo(0, ID_PS_EDITOR_STATUS, SBPS_NORMAL, computeStatusBarWidth());
|
||
|
RepositionBars(AFX_IDW_CONTROLBAR_FIRST, AFX_IDW_CONTROLBAR_LAST, ID_PS_EDITOR_STATUS);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//**************************************************************************************************************************
|
||
|
CRect CParticleDlg::getTreeRect(int cx, int cy) const
|
||
|
{
|
||
|
const uint ox = 0, oy = 10;
|
||
|
|
||
|
if (CurrentRightPane)
|
||
|
{
|
||
|
CRect res(ox, oy, cx - CurrRightPaneWidth - 10, cy - 20);
|
||
|
return res;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
CRect res(ox, oy, cx - 10, cy - 20);
|
||
|
return res;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//**************************************************************************************************************************
|
||
|
void CParticleDlg::setRightPane(CWnd *pane)
|
||
|
{
|
||
|
AFX_MANAGE_STATE(AfxGetStaticModuleState());
|
||
|
if (CurrentRightPane)
|
||
|
{
|
||
|
CurrentRightPane->DestroyWindow();
|
||
|
}
|
||
|
delete CurrentRightPane;
|
||
|
CurrentRightPane = pane;
|
||
|
RECT r;
|
||
|
if (pane)
|
||
|
{
|
||
|
|
||
|
pane->ShowWindow(SW_SHOW);
|
||
|
|
||
|
|
||
|
CurrentRightPane->GetClientRect(&r);
|
||
|
|
||
|
CurrRightPaneWidth = r.right;
|
||
|
CurrRightPaneHeight = r.bottom;
|
||
|
|
||
|
}
|
||
|
|
||
|
GetClientRect(&r);
|
||
|
this->SendMessage(WM_SIZE, SIZE_RESTORED, r.right + (r.bottom << 16));
|
||
|
GetWindowRect(&r);
|
||
|
this->MoveWindow(&r);
|
||
|
if (CurrentRightPane)
|
||
|
{
|
||
|
CurrentRightPane->Invalidate();
|
||
|
}
|
||
|
this->Invalidate();
|
||
|
ParticleTreeCtrl->Invalidate();
|
||
|
}
|
||
|
|
||
|
//**************************************************************************************************************************
|
||
|
LRESULT CParticleDlg::WindowProc(UINT message, WPARAM wParam, LPARAM lParam)
|
||
|
{
|
||
|
|
||
|
if (message == WM_GETMINMAXINFO)
|
||
|
{
|
||
|
sint cx = 150, cy = 150;
|
||
|
if (CurrentRightPane)
|
||
|
{
|
||
|
RECT r;
|
||
|
CurrentRightPane->GetClientRect(&r);
|
||
|
cx += CurrRightPaneWidth;
|
||
|
if (cy < (CurrRightPaneHeight + 20) ) cy = CurrRightPaneHeight + 20;
|
||
|
}
|
||
|
|
||
|
|
||
|
MINMAXINFO *inf = (MINMAXINFO *) lParam;
|
||
|
inf->ptMinTrackSize.x = cx;
|
||
|
inf->ptMinTrackSize.y = cy;
|
||
|
|
||
|
}
|
||
|
|
||
|
return CDialog::WindowProc(message, wParam, lParam);
|
||
|
|
||
|
}
|
||
|
|
||
|
//**************************************************************************************************************************
|
||
|
void CParticleDlg::OnShowWindow(BOOL bShow, UINT nStatus)
|
||
|
{
|
||
|
CDialog::OnShowWindow(bShow, nStatus);
|
||
|
StartStopDlg->ShowWindow(bShow);
|
||
|
|
||
|
}
|
||
|
|
||
|
//**************************************************************************************************************************
|
||
|
void CParticleDlg::goPostRender()
|
||
|
{
|
||
|
NL3D::CParticleSystem *currPS = _ActiveNode ? _ActiveNode->getPSPointer() : NULL;
|
||
|
if (!currPS) return;
|
||
|
if (StartStopDlg->isBBoxDisplayEnabled() && currPS)
|
||
|
{
|
||
|
NL3D::CNELU::Driver->setupModelMatrix(currPS->getSysMat());
|
||
|
if (_AutoUpdateBBox)
|
||
|
{
|
||
|
NLMISC::CAABBox currBBox;
|
||
|
currPS->forceComputeBBox(currBBox);
|
||
|
if (_EmptyBBox)
|
||
|
{
|
||
|
_EmptyBBox = false;
|
||
|
_CurrBBox = currBBox;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
NL3D::CPSUtil::displayBBox(NL3D::CNELU::Driver, _CurrBBox, CRGBA::Blue);
|
||
|
_CurrBBox = NLMISC::CAABBox::computeAABBoxUnion(currBBox, _CurrBBox);
|
||
|
}
|
||
|
currPS->setPrecomputedBBox(_CurrBBox);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
currPS->getLastComputedBBox(_CurrBBox);
|
||
|
}
|
||
|
NL3D::CPSUtil::displayBBox(NL3D::CNELU::Driver, _CurrBBox, currPS->getAutoComputeBBox() ? CRGBA::White : CRGBA::Red);
|
||
|
}
|
||
|
// copy user matrix into current fx
|
||
|
nlassert(_ObjView);
|
||
|
_ActiveNode->getPSModel()->setUserMatrix(_ObjView->getFXUserMatrix());
|
||
|
}
|
||
|
|
||
|
//**************************************************************************************************************************
|
||
|
void CParticleDlg::OnChar(UINT nChar, UINT nRepCnt, UINT nFlags)
|
||
|
{
|
||
|
if (nChar == (UINT) 'p' || nChar == (UINT) 'P' || nChar == (UINT) ' ')
|
||
|
{
|
||
|
// simulate a start / stop on the system
|
||
|
StartStopDlg->toggle();
|
||
|
}
|
||
|
|
||
|
CDialog::OnChar(nChar, nRepCnt, nFlags);
|
||
|
}
|
||
|
|
||
|
//**************************************************************************************************************************
|
||
|
const NLMISC::CMatrix &CParticleDlg::getPSMatrix() const
|
||
|
{
|
||
|
if (!getActivePSM()) return NLMISC::CMatrix::Identity;
|
||
|
return getActivePSM()->getMatrix();
|
||
|
}
|
||
|
|
||
|
//**************************************************************************************************************************
|
||
|
const NLMISC::CMatrix &CParticleDlg::getPSWorldMatrix() const
|
||
|
{
|
||
|
if (!getActivePSM()) return NLMISC::CMatrix::Identity;
|
||
|
return getActivePSM()->getWorldMatrix();
|
||
|
}
|
||
|
|
||
|
//**************************************************************************************************************************
|
||
|
void CParticleDlg::setPSMatrix(const NLMISC::CMatrix &mat)
|
||
|
{
|
||
|
if (!getActivePSM()) return;
|
||
|
getActivePSM()->setMatrix(mat);
|
||
|
}
|
||
|
|
||
|
//**************************************************************************************************************************
|
||
|
void CParticleDlg::setPSWorldMatrix(const NLMISC::CMatrix &mat)
|
||
|
{
|
||
|
if (!getActivePSM()) return;
|
||
|
CMatrix invParentMat = getActivePSM()->getMatrix() * getActivePSM()->getWorldMatrix().inverted();
|
||
|
invParentMat.normalize(CMatrix::XYZ);
|
||
|
CMatrix newMat = invParentMat * mat;
|
||
|
newMat.normalize(CMatrix::XYZ);
|
||
|
getActivePSM()->setMatrix(newMat);
|
||
|
}
|
||
|
|
||
|
|
||
|
//**************************************************************************************************************************
|
||
|
void CParticleDlg::stickPSToSkeleton(CParticleWorkspace::CNode *node,
|
||
|
NL3D::CSkeletonModel *skel,
|
||
|
uint bone,
|
||
|
const std::string &parentSkelName,
|
||
|
const std::string &parentBoneName)
|
||
|
{
|
||
|
if (!node) return;
|
||
|
node->stickPSToSkeleton(skel, bone, parentSkelName, parentBoneName);
|
||
|
if (skel)
|
||
|
{
|
||
|
if (_ObjView->getMainFrame()->MouseMoveType == CMainFrame::MoveFX)
|
||
|
{
|
||
|
_ObjView->getMainFrame()->OnEditMovecamera();
|
||
|
}
|
||
|
_ObjView->getMainFrame()->ToolBar.Invalidate();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//**************************************************************************************************************************
|
||
|
void CParticleDlg::unstickPSFromSkeleton(CParticleWorkspace::CNode *node)
|
||
|
{
|
||
|
if (!node) return;
|
||
|
node->unstickPSFromSkeleton();
|
||
|
_ObjView->getMainFrame()->ToolBar.Invalidate();
|
||
|
}
|
||
|
|
||
|
//**************************************************************************************************************************
|
||
|
bool CParticleDlg::savePS(HWND parent, CParticleWorkspace::CNode &psNode, bool askToContinueWhenError)
|
||
|
{
|
||
|
return savePSAs(parent, psNode, psNode.getFullPath(), askToContinueWhenError);
|
||
|
}
|
||
|
|
||
|
//**************************************************************************************************************************
|
||
|
bool CParticleDlg::savePSAs(HWND parent, CParticleWorkspace::CNode &psNode ,const std::string &fullPath, bool askToContinueWhenError)
|
||
|
{
|
||
|
nlassert(psNode.getPSPointer());
|
||
|
if (psNode.getResetAutoCountFlag() && psNode.getPSPointer()->getAutoCountFlag())
|
||
|
{
|
||
|
MessageBox(psNode.getFilename().c_str() + getStrRsc(IDS_AUTO_COUNT_ERROR), getStrRsc(IDS_WARNING), MB_ICONEXCLAMATION);
|
||
|
return false;
|
||
|
}
|
||
|
StartStopDlg->stop();
|
||
|
try
|
||
|
{
|
||
|
psNode.savePSAs(fullPath);
|
||
|
psNode.setModified(false);
|
||
|
setStatusBarText(CString(fullPath.c_str()) + " " + getStrRsc(IDS_SAVED));
|
||
|
}
|
||
|
catch (NLMISC::Exception &e)
|
||
|
{
|
||
|
if (askToContinueWhenError)
|
||
|
{
|
||
|
int result = ::MessageBox(parent, CString(e.what()) + getStrRsc(IDS_CONTINUE_SAVING) , getStrRsc(IDS_ERROR), MB_ICONEXCLAMATION);
|
||
|
return result == MB_OK;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
::MessageBox(parent, e.what(), getStrRsc(IDS_ERROR), MB_ICONEXCLAMATION);
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
//**************************************************************************************************************************
|
||
|
bool CParticleDlg::loadPS(HWND parent, CParticleWorkspace::CNode &psNode, TLoadPSBehav behav)
|
||
|
{
|
||
|
bool loadingOK = true;
|
||
|
try
|
||
|
{
|
||
|
if (!psNode.loadPS())
|
||
|
{
|
||
|
loadingOK = false;
|
||
|
switch(behav)
|
||
|
{
|
||
|
case Silent: return false; // no op
|
||
|
case ReportError:
|
||
|
::MessageBox(parent, (LPCTSTR) (CString(psNode.getFilename().c_str()) + " : " + getStrRsc(IDS_COULDNT_INSTANCIATE_PS)), getStrRsc(IDS_ERROR), MB_OK|MB_ICONEXCLAMATION);
|
||
|
return true;
|
||
|
break;
|
||
|
case ReportErrorSkippable:
|
||
|
{
|
||
|
CSkippableMessageBox mb(getStrRsc(IDS_ERROR), CString(psNode.getFilename().c_str()) + " : " + getStrRsc(IDS_COULDNT_INSTANCIATE_PS), CWnd::FromHandle(parent));
|
||
|
mb.DoModal();
|
||
|
return !mb.getBypassFlag();
|
||
|
}
|
||
|
break;
|
||
|
default:
|
||
|
nlassert(0);
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
setStatusBarText(CString(psNode.getFullPath().c_str()) + " " + getStrRsc(IDS_LOADED));
|
||
|
}
|
||
|
}
|
||
|
catch (NLMISC::Exception &e)
|
||
|
{
|
||
|
switch(behav)
|
||
|
{
|
||
|
case Silent: return false; // no op
|
||
|
case ReportError:
|
||
|
::MessageBox(parent, e.what(), getStrRsc(IDS_ERROR), MB_OK|MB_ICONEXCLAMATION);
|
||
|
return true;
|
||
|
break;
|
||
|
case ReportErrorSkippable:
|
||
|
{
|
||
|
CSkippableMessageBox mb(getStrRsc(IDS_ERROR), CString(e.what()), CWnd::FromHandle(parent));
|
||
|
mb.DoModal();
|
||
|
return !mb.getBypassFlag();
|
||
|
}
|
||
|
break;
|
||
|
default:
|
||
|
nlassert(0);
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
if (psNode.getPSPointer()->hasLoop())
|
||
|
{
|
||
|
localizedMessageBox(parent, IDS_FX_HAS_LOOP, IDS_WARNING, MB_OK|MB_ICONEXCLAMATION);
|
||
|
}
|
||
|
return behav != Silent;
|
||
|
}
|
||
|
|
||
|
|
||
|
//**************************************************************************************************************************
|
||
|
void CParticleDlg::checkModifiedWorkSpace()
|
||
|
{
|
||
|
if (_PW)
|
||
|
{
|
||
|
// see if current tree has been changed
|
||
|
if (_PW->isModified())
|
||
|
{
|
||
|
int result = localizedMessageBox(*this, IDS_PS_WORKSPACE_MODIFIED, IDS_PARTICLE_EDITOR, MB_YESNO|MB_ICONQUESTION);
|
||
|
if (result == IDYES)
|
||
|
{
|
||
|
saveWorkspaceStructure();
|
||
|
}
|
||
|
}
|
||
|
if (_PW->isContentModified())
|
||
|
{
|
||
|
saveWorkspaceContent(true);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//**************************************************************************************************************************
|
||
|
void CParticleDlg::closeWorkspace()
|
||
|
{
|
||
|
setActiveNode(NULL);
|
||
|
ParticleTreeCtrl->setActiveNode(NULL);
|
||
|
ParticleTreeCtrl->reset();
|
||
|
delete _PW;
|
||
|
_PW = NULL;
|
||
|
}
|
||
|
|
||
|
//**************************************************************************************************************************
|
||
|
void CParticleDlg::OnCreateNewPsWorkspace()
|
||
|
{
|
||
|
checkModifiedWorkSpace();
|
||
|
// ask name of the new workspace to create
|
||
|
CCreateFileDlg cf(getStrRsc(IDS_CHOOSE_WORKSPACE_NAME), "", "pws");
|
||
|
INT_PTR result = cf.DoModal();
|
||
|
if (result = IDOK)
|
||
|
{
|
||
|
if (cf.touchFile())
|
||
|
{
|
||
|
CParticleWorkspace *newPW = new CParticleWorkspace;
|
||
|
newPW->setModificationCallback(ParticleTreeCtrl);
|
||
|
newPW->init(_ObjView, cf.getFullPath(), _ObjView->getFontManager(), _ObjView->getFontGenerator());
|
||
|
// save empty workspace
|
||
|
try
|
||
|
{
|
||
|
newPW->save();
|
||
|
}
|
||
|
catch(NLMISC::EStream &e)
|
||
|
{
|
||
|
MessageBox(e.what(), getStrRsc(IDS_ERROR), MB_ICONEXCLAMATION);
|
||
|
}
|
||
|
closeWorkspace();
|
||
|
_PW = newPW;
|
||
|
ParticleTreeCtrl->buildTreeFromWorkSpace(*_PW);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
//**************************************************************************************************************************
|
||
|
void CParticleDlg::OnLoadPSWorkspace()
|
||
|
{
|
||
|
checkModifiedWorkSpace();
|
||
|
static const char BASED_CODE szFilter[] = "particle workspaces(*.pws)|*.pws||";
|
||
|
CFileDialog fd( TRUE, ".pws", "*.pws", 0, szFilter);
|
||
|
INT_PTR result = fd.DoModal();
|
||
|
if (result != IDOK) return;
|
||
|
loadWorkspace((LPCTSTR) fd.GetPathName());
|
||
|
}
|
||
|
|
||
|
//**************************************************************************************************************************
|
||
|
void CParticleDlg::loadWorkspace(const std::string &fullPath)
|
||
|
{
|
||
|
// Add to the path
|
||
|
std::auto_ptr<CParticleWorkspace> newPW(new CParticleWorkspace);
|
||
|
newPW->init(_ObjView, fullPath, _ObjView->getFontManager(), _ObjView->getFontGenerator());
|
||
|
newPW->setModificationCallback(ParticleTreeCtrl);
|
||
|
// save empty workspace
|
||
|
try
|
||
|
{
|
||
|
newPW->load();
|
||
|
setStatusBarText(CString(newPW->getFilename().c_str()) + " " + getStrRsc(IDS_LOADED));
|
||
|
}
|
||
|
catch(NLMISC::EStream &e)
|
||
|
{
|
||
|
MessageBox(e.what(), getStrRsc(IDS_ERROR), MB_ICONEXCLAMATION);
|
||
|
setStatusBarText(CString(e.what()));
|
||
|
return;
|
||
|
}
|
||
|
// try to load each ps
|
||
|
CParticleWorkspace::CNode *firstLoadedNode = NULL;
|
||
|
bool displayErrorMsg = true;
|
||
|
for(uint k = 0; k < newPW->getNumNode(); ++k)
|
||
|
{
|
||
|
|
||
|
displayErrorMsg = loadPS(*this, *newPW->getNode(k), displayErrorMsg ? ReportErrorSkippable : Silent);
|
||
|
if (newPW->getNode(k)->isLoaded() && !firstLoadedNode)
|
||
|
{
|
||
|
firstLoadedNode = newPW->getNode(k);
|
||
|
}
|
||
|
}
|
||
|
closeWorkspace();
|
||
|
_PW = newPW.release();
|
||
|
ParticleTreeCtrl->buildTreeFromWorkSpace(*_PW);
|
||
|
setActiveNode(firstLoadedNode);
|
||
|
ParticleTreeCtrl->setActiveNode(firstLoadedNode);
|
||
|
ParticleTreeCtrl->expandRoot();
|
||
|
setStatusBarText(getStrRsc(IDS_READY));
|
||
|
}
|
||
|
|
||
|
//**************************************************************************************************************************
|
||
|
void CParticleDlg::OnSaveAllPsWorkspace()
|
||
|
{
|
||
|
saveWorkspaceStructure();
|
||
|
saveWorkspaceContent(false);
|
||
|
}
|
||
|
|
||
|
//**************************************************************************************************************************
|
||
|
void CParticleDlg::OnSavePsWorkspace()
|
||
|
{
|
||
|
saveWorkspaceStructure();
|
||
|
saveWorkspaceContent(true);
|
||
|
}
|
||
|
|
||
|
//**************************************************************************************************************************
|
||
|
void CParticleDlg::saveWorkspaceStructure()
|
||
|
{
|
||
|
nlassert(_PW);
|
||
|
try
|
||
|
{
|
||
|
_PW->save();
|
||
|
setStatusBarText(CString(_PW->getFilename().c_str()) + " " + getStrRsc(IDS_SAVED));
|
||
|
}
|
||
|
catch(NLMISC::EStream &e)
|
||
|
{
|
||
|
localizedMessageBox(*this, e.what(), IDS_ERROR, MB_ICONEXCLAMATION);
|
||
|
setStatusBarText(CString(e.what()));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
//**************************************************************************************************************************
|
||
|
void CParticleDlg::saveWorkspaceContent(bool askToSaveModifiedPS)
|
||
|
{
|
||
|
StartStopDlg->stop();
|
||
|
bool saveAll = !askToSaveModifiedPS;
|
||
|
// check each component of the tree
|
||
|
for(uint k = 0; k < _PW->getNumNode(); ++k)
|
||
|
{
|
||
|
if (_PW->getNode(k)->isModified())
|
||
|
{
|
||
|
if (saveAll)
|
||
|
{
|
||
|
bool keepSaving = savePS(*this, *_PW->getNode(k), k != _PW->getNumNode());
|
||
|
if (!keepSaving) break;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// ask if the user wants to save the ps, or save all ps
|
||
|
CString mess;
|
||
|
mess = CString(_PW->getNode(k)->getFilename().c_str()) + getStrRsc(IDS_SAVE_MODIFIED_PS);
|
||
|
CSaveOptionsDlg sop(getStrRsc(IDS_SAVE_FILE), mess, this);
|
||
|
sop.DoModal();
|
||
|
bool saveThisFile = false;
|
||
|
bool stop = false;
|
||
|
switch(sop.getChoice())
|
||
|
{
|
||
|
case CSaveOptionsDlg::Yes: saveThisFile = true; break;
|
||
|
case CSaveOptionsDlg::No: saveThisFile = false; break;
|
||
|
case CSaveOptionsDlg::SaveAll: saveAll = true; break;
|
||
|
case CSaveOptionsDlg::Stop: stop = true; break;
|
||
|
default: nlassert(0);
|
||
|
}
|
||
|
if (stop) break;
|
||
|
if (saveAll || saveThisFile)
|
||
|
{
|
||
|
bool keepSaving = savePS(*this, *_PW->getNode(k), k != _PW->getNumNode());
|
||
|
if (!keepSaving) break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
setStatusBarText(getStrRsc(IDS_READY));
|
||
|
}
|
||
|
|
||
|
//**************************************************************************************************************************
|
||
|
void CParticleDlg::setActiveNode(CParticleWorkspace::CNode *node)
|
||
|
{
|
||
|
if (node == _ActiveNode) return;
|
||
|
_ActiveNode = node;
|
||
|
StartStopDlg->setActiveNode(node);
|
||
|
if(MainFrame->isMoveFX())
|
||
|
{
|
||
|
if (node)
|
||
|
{
|
||
|
_ObjView->getMouseListener().setModelMatrix(node->getPSModel()->getMatrix());
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
_ObjView->getMouseListener().setModelMatrix(NLMISC::CMatrix::Identity);
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
if(MainFrame->isMoveFXUserMatrix())
|
||
|
{
|
||
|
if (node)
|
||
|
{
|
||
|
_ObjView->getMouseListener().setModelMatrix(node->getPSModel()->getUserMatrix());
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
_ObjView->getMouseListener().setModelMatrix(NLMISC::CMatrix::Identity);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//**************************************************************************************************************************
|
||
|
NL3D::CParticleSystemModel *CParticleDlg::getModelFromPS(NL3D::CParticleSystem *ps) const
|
||
|
{
|
||
|
if (!ps) return NULL;
|
||
|
if (!_PW) return NULL;
|
||
|
CParticleWorkspace::CNode *node = _PW->getNodeFromPS(ps);
|
||
|
if (!node) return NULL;
|
||
|
return node->getPSModel();
|
||
|
}
|
||
|
|
||
|
//**************************************************************************************************************************
|
||
|
uint CParticleDlg::computeStatusBarWidth() const
|
||
|
{
|
||
|
nlassert(ParticleTreeCtrl);
|
||
|
CRect tcRect;
|
||
|
ParticleTreeCtrl->GetClientRect(&tcRect);
|
||
|
if (!CurrentRightPane) return (uint) std::max((sint) tcRect.Width() - 16, (sint) 0);
|
||
|
return (uint) std::max((sint) tcRect.Width() - 4, (sint) 0);
|
||
|
}
|
||
|
|
||
|
//**************************************************************************************************************************
|
||
|
void CParticleDlg::OnViewPsFilename()
|
||
|
{
|
||
|
ParticleTreeCtrl->setViewFilenameFlag(!ParticleTreeCtrl->getViewFilenameFlag());
|
||
|
updateMenu();
|
||
|
}
|
||
|
|
||
|
//**************************************************************************************************************************
|
||
|
void CParticleDlg::updateMenu()
|
||
|
{
|
||
|
if (!ParticleTreeCtrl) return;
|
||
|
CMenu *menu = GetMenu();
|
||
|
if (!menu) return;
|
||
|
// update the view menu
|
||
|
menu->CheckMenuItem(IDM_VIEW_PS_FILENAME, MF_BYCOMMAND|(ParticleTreeCtrl->getViewFilenameFlag() ? MF_CHECKED : 0));
|
||
|
}
|
||
|
|
||
|
|