mirror of
https://port.numenaute.org/aleajactaest/khanat-opennel-code.git
synced 2024-12-21 00:18:43 +00:00
592 lines
17 KiB
C++
592 lines
17 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 <shlwapi.h>
|
|
//
|
|
#include "particle_workspace.h"
|
|
#include "object_viewer.h"
|
|
//
|
|
#include "nel/3d/shape_bank.h"
|
|
#include "nel/3d/particle_system_model.h"
|
|
#include "nel/3d/particle_system_shape.h"
|
|
#include "nel/3d/skeleton_model.h"
|
|
//
|
|
#include "nel/misc/o_xml.h"
|
|
#include "nel/misc/i_xml.h"
|
|
#include "nel/misc/file.h"
|
|
//
|
|
|
|
|
|
|
|
//***********************************************************************************************
|
|
CParticleWorkspace::CNode::CNode()
|
|
{
|
|
_PS = NULL;
|
|
_PSM = NULL;
|
|
_ShapeBank = NULL;
|
|
_Modified = false;
|
|
_ParentSkel = NULL;
|
|
_ResetAutoCount = false;
|
|
_WS = NULL;
|
|
}
|
|
|
|
//***********************************************************************************************
|
|
CParticleWorkspace::CNode::~CNode()
|
|
{
|
|
unload();
|
|
|
|
}
|
|
|
|
//***********************************************************************************************
|
|
void CParticleWorkspace::CNode::memorizeState()
|
|
{
|
|
nlassert(_WS);
|
|
if (!_PS) return;
|
|
_InitialPos.copySystemInitialPos(_PS);
|
|
}
|
|
|
|
//***********************************************************************************************
|
|
void CParticleWorkspace::CNode::restoreState()
|
|
{
|
|
nlassert(_WS);
|
|
if (!_PS) return;
|
|
_InitialPos.restoreSystem();
|
|
}
|
|
|
|
//**************************************************************************************************************************
|
|
bool CParticleWorkspace::CNode::isStateMemorized() const
|
|
{
|
|
return _InitialPos.isStateMemorized();
|
|
}
|
|
|
|
//**************************************************************************************************************************
|
|
void CParticleWorkspace::CNode::stickPSToSkeleton(NL3D::CSkeletonModel *skel,
|
|
uint bone,
|
|
const std::string &parentSkelName,
|
|
const std::string &parentBoneName)
|
|
{
|
|
nlassert(_WS);
|
|
if (!_PSM) return;
|
|
unstickPSFromSkeleton();
|
|
_ParentSkelName = parentSkelName;
|
|
_ParentBoneName = parentBoneName;
|
|
if (skel)
|
|
{
|
|
skel->stickObject(_PSM, bone);
|
|
_PSM->setMatrix(NLMISC::CMatrix::Identity);
|
|
_ParentSkel = skel;
|
|
}
|
|
if (_WS->getModificationCallback())
|
|
{
|
|
_WS->getModificationCallback()->nodeSkelParentChanged(*this);
|
|
}
|
|
}
|
|
|
|
//**************************************************************************************************************************
|
|
void CParticleWorkspace::CNode::unstickPSFromSkeleton()
|
|
{
|
|
nlassert(_WS);
|
|
_ParentSkelName = "";
|
|
_ParentBoneName = "";
|
|
if (!_PSM) return;
|
|
if (_ParentSkel)
|
|
{
|
|
_ParentSkel->detachSkeletonSon(_PSM);
|
|
_ParentSkel = NULL;
|
|
}
|
|
}
|
|
|
|
//***********************************************************************************************
|
|
void CParticleWorkspace::CNode::removeLocated(NL3D::CPSLocated *loc)
|
|
{
|
|
nlassert(_WS);
|
|
if (_InitialPos.isStateMemorized())
|
|
{
|
|
_InitialPos.removeLocated(loc);
|
|
}
|
|
}
|
|
|
|
//***********************************************************************************************
|
|
void CParticleWorkspace::CNode::removeLocatedBindable(NL3D::CPSLocatedBindable *lb)
|
|
{
|
|
nlassert(_WS);
|
|
if (_InitialPos.isStateMemorized())
|
|
{
|
|
_InitialPos.removeLocatedBindable(lb);
|
|
}
|
|
}
|
|
|
|
//***********************************************************************************************
|
|
void CParticleWorkspace::CNode::setModified(bool modified)
|
|
{
|
|
nlassert(_WS);
|
|
if (_Modified == modified) return;
|
|
_Modified = modified;
|
|
_WS->nodeModified(*this);
|
|
}
|
|
|
|
//***********************************************************************************************
|
|
void CParticleWorkspace::CNode::unload()
|
|
{
|
|
nlassert(_WS);
|
|
if (_PSM)
|
|
{
|
|
NL3D::CShapeBank *oldSB = NL3D::CNELU::Scene->getShapeBank();
|
|
NL3D::CNELU::Scene->setShapeBank(_ShapeBank);
|
|
NL3D::CNELU::Scene->deleteInstance(_PSM);
|
|
NL3D::CNELU::Scene->setShapeBank(oldSB);
|
|
_PSM = NULL;
|
|
}
|
|
delete _ShapeBank;
|
|
_ShapeBank = NULL;
|
|
_PS = NULL;
|
|
_ParentSkel = NULL;
|
|
}
|
|
|
|
|
|
//***********************************************************************************************
|
|
void CParticleWorkspace::CNode::setup(NL3D::CParticleSystemModel &psm)
|
|
{
|
|
nlassert(_WS);
|
|
psm.setTransformMode(NL3D::CTransform::DirectMatrix);
|
|
psm.setMatrix(NLMISC::CMatrix::Identity);
|
|
nlassert(_WS->getObjectViewer());
|
|
psm.setEditionMode(true); // this also force the system instanciation
|
|
for(uint k = 0; k < NL3D::MaxPSUserParam; ++k)
|
|
{
|
|
psm.bypassGlobalUserParamValue(k);
|
|
}
|
|
psm.enableAutoGetEllapsedTime(false);
|
|
psm.setEllapsedTime(0.f); // system is paused
|
|
// initialy, the ps is hidden
|
|
psm.hide();
|
|
// link to the root for manipulation
|
|
_WS->getObjectViewer()->getSceneRoot()->hrcLinkSon(&psm);
|
|
NL3D::CParticleSystem *ps = psm.getPS();
|
|
nlassert(ps);
|
|
ps->setFontManager(_WS->getFontManager());
|
|
ps->setFontGenerator(_WS->getFontGenerator());
|
|
ps->stopSound();
|
|
// flush textures
|
|
psm.Shape->flushTextures(*NL3D::CNELU::Driver, 0);
|
|
}
|
|
|
|
//***********************************************************************************************
|
|
void CParticleWorkspace::CNode::setTriggerAnim(const std::string &anim)
|
|
{
|
|
nlassert(_WS);
|
|
if (anim == _TriggerAnim) return;
|
|
_WS->touch();
|
|
_TriggerAnim = anim;
|
|
}
|
|
|
|
|
|
//***********************************************************************************************
|
|
void CParticleWorkspace::CNode::createEmptyPS()
|
|
{
|
|
nlassert(_WS);
|
|
NL3D::CParticleSystem emptyPS;
|
|
NL3D::CParticleSystemShape *pss = new NL3D::CParticleSystemShape;
|
|
pss->buildFromPS(emptyPS);
|
|
std::auto_ptr<NL3D::CShapeBank> sb(new NL3D::CShapeBank);
|
|
std::string shapeName = NLMISC::CFile::getFilename(_RelativePath);
|
|
sb->add(shapeName, pss);
|
|
NL3D::CShapeBank *oldSB = NL3D::CNELU::Scene->getShapeBank();
|
|
NL3D::CNELU::Scene->setShapeBank(sb.get());
|
|
NL3D::CParticleSystemModel *psm = NLMISC::safe_cast<NL3D::CParticleSystemModel *>(NL3D::CNELU::Scene->createInstance(shapeName));
|
|
nlassert(psm);
|
|
NL3D::CNELU::Scene->setShapeBank(oldSB);
|
|
setup(*psm);
|
|
unload();
|
|
// commit new values
|
|
_PS = psm->getPS();
|
|
_PSM = psm;
|
|
_ShapeBank = sb.release();
|
|
_Modified = false;
|
|
}
|
|
|
|
//***********************************************************************************************
|
|
void CParticleWorkspace::CNode::init(CParticleWorkspace *ws)
|
|
{
|
|
nlassert(ws);
|
|
_WS = ws;
|
|
}
|
|
|
|
//***********************************************************************************************
|
|
void CParticleWorkspace::CNode::setRelativePath(const std::string &relativePath)
|
|
{
|
|
nlassert(_WS);
|
|
_RelativePath = relativePath;
|
|
}
|
|
|
|
//***********************************************************************************************
|
|
void CParticleWorkspace::CNode::serial(NLMISC::IStream &f)
|
|
{
|
|
nlassert(_WS);
|
|
f.xmlPush("PROJECT_FILE");
|
|
sint version = f.serialVersion(2);
|
|
f.xmlSerial(_RelativePath, "RELATIVE_PATH");
|
|
if (version >= 1)
|
|
{
|
|
f.xmlSerial(_TriggerAnim, "TRIGGER_ANIMATION");
|
|
}
|
|
if (version >= 2)
|
|
{
|
|
f.xmlSerial(_ParentSkelName, "PARENT_SKEL_NAME");
|
|
f.xmlSerial(_ParentBoneName, "PARENT_BONE_NAME");
|
|
}
|
|
f.xmlPop();
|
|
}
|
|
|
|
//***********************************************************************************************
|
|
void CParticleWorkspace::CNode::savePS()
|
|
{
|
|
savePSAs(getFullPath());
|
|
}
|
|
|
|
|
|
//***********************************************************************************************
|
|
void CParticleWorkspace::CNode::savePSAs(const std::string &fullPath) throw(NLMISC::EStream)
|
|
{
|
|
nlassert(_WS);
|
|
if (!_PS) return;
|
|
// build a shape from our system, and save it
|
|
NL3D::CParticleSystemShape psc;
|
|
psc.buildFromPS(*_PS);
|
|
NL3D::CShapeStream st(&psc);
|
|
NLMISC::COFile oFile(fullPath);
|
|
oFile.serial(st);
|
|
}
|
|
|
|
//***********************************************************************************************
|
|
std::string CParticleWorkspace::CNode::getFullPath() const
|
|
{
|
|
nlassert(_WS);
|
|
char fullPath[MAX_PATH];
|
|
std::string basePath = NLMISC::CPath::standardizeDosPath(_WS->getPath());
|
|
if (PathCombine(fullPath, basePath.c_str(), _RelativePath.c_str()))
|
|
{
|
|
return fullPath;
|
|
}
|
|
else
|
|
{
|
|
return _RelativePath.c_str();
|
|
}
|
|
}
|
|
|
|
//***********************************************************************************************
|
|
bool CParticleWorkspace::CNode::loadPS()
|
|
{
|
|
nlassert(_WS);
|
|
// manually load the PS shape (so that we can deal with exceptions)
|
|
NL3D::CShapeStream ss;
|
|
NLMISC::CIFile inputFile;
|
|
// collapse name
|
|
inputFile.open(getFullPath());
|
|
ss.serial(inputFile);
|
|
std::auto_ptr<NL3D::CShapeBank> sb(new NL3D::CShapeBank);
|
|
std::string shapeName = NLMISC::CFile::getFilename(_RelativePath);
|
|
sb->add(shapeName, ss.getShapePointer());
|
|
NL3D::CShapeBank *oldSB = NL3D::CNELU::Scene->getShapeBank();
|
|
NL3D::CNELU::Scene->setShapeBank(sb.get());
|
|
NL3D::CTransformShape *trs = NL3D::CNELU::Scene->createInstance(shapeName);
|
|
if (!trs)
|
|
{
|
|
NL3D::CNELU::Scene->setShapeBank(oldSB);
|
|
return false;
|
|
}
|
|
NL3D::CParticleSystemModel *psm = dynamic_cast<NL3D::CParticleSystemModel *>(trs);
|
|
if (!psm)
|
|
{
|
|
// Not a particle system
|
|
NL3D::CNELU::Scene->deleteInstance(trs);
|
|
return false;
|
|
}
|
|
NL3D::CNELU::Scene->setShapeBank(oldSB);
|
|
setup(*psm);
|
|
unload();
|
|
// commit new values
|
|
_PS = psm->getPS();
|
|
_PSM = psm;
|
|
_ShapeBank = sb.release();
|
|
_Modified = false;
|
|
return true;
|
|
}
|
|
|
|
|
|
//***********************************************************************************************
|
|
CParticleWorkspace::CParticleWorkspace()
|
|
{
|
|
_OV = NULL;
|
|
_Modified = false;
|
|
_FontManager = NULL;
|
|
_FontGenerator = NULL;
|
|
_ModificationCallback = NULL;
|
|
}
|
|
|
|
//***********************************************************************************************
|
|
CParticleWorkspace::~CParticleWorkspace()
|
|
{
|
|
}
|
|
|
|
//***********************************************************************************************
|
|
void CParticleWorkspace::init(CObjectViewer *ov,
|
|
const std::string &filename,
|
|
NL3D::CFontManager *fontManager,
|
|
NL3D::CFontGenerator *fontGenerator
|
|
)
|
|
{
|
|
nlassert(!_OV);
|
|
nlassert(ov);
|
|
_OV = ov;
|
|
_Filename = filename;
|
|
_FontManager = fontManager;
|
|
_FontGenerator = fontGenerator;
|
|
}
|
|
//***********************************************************************************************
|
|
void CParticleWorkspace::setName(const std::string &name)
|
|
{
|
|
_Name = name;
|
|
touch();
|
|
}
|
|
|
|
|
|
//***********************************************************************************************
|
|
CParticleWorkspace::CNode *CParticleWorkspace::addNode(const std::string &filenameWithFullPath) throw( NLMISC::EStream)
|
|
{
|
|
nlassert(_OV);
|
|
// Check that file is not already inserted
|
|
std::string fileName = NLMISC::CFile::getFilename(filenameWithFullPath);
|
|
for(uint k = 0; k < _Nodes.size(); ++k)
|
|
{
|
|
if (NLMISC::nlstricmp(_Nodes[k]->getFilename(), fileName) == 0) return NULL;
|
|
}
|
|
char resultPath[MAX_PATH];
|
|
std::string dosPath = NLMISC::CPath::standardizeDosPath(getPath());
|
|
std::string relativePath;
|
|
if (!PathRelativePathTo(resultPath, dosPath.c_str(), FILE_ATTRIBUTE_DIRECTORY, filenameWithFullPath.c_str(), 0))
|
|
{
|
|
relativePath = filenameWithFullPath;
|
|
}
|
|
else
|
|
{
|
|
relativePath = resultPath;
|
|
}
|
|
if (relativePath.size() >= 2)
|
|
{
|
|
if (relativePath[0] == '\\' && relativePath[1] != '\\')
|
|
{
|
|
relativePath = relativePath.substr(1);
|
|
}
|
|
}
|
|
CNode *newNode = new CNode;
|
|
newNode->init(this);
|
|
newNode->setRelativePath(relativePath);
|
|
_Nodes.push_back(newNode);
|
|
setModifiedFlag(true);
|
|
return newNode;
|
|
}
|
|
|
|
//***********************************************************************************************
|
|
void CParticleWorkspace::removeNode(uint index)
|
|
{
|
|
nlassert(_OV);
|
|
nlassert(index < _Nodes.size());
|
|
_Nodes[index] = NULL; // delete the smart-ptr target
|
|
_Nodes.erase(_Nodes.begin() + index);
|
|
touch();
|
|
}
|
|
|
|
//***********************************************************************************************
|
|
void CParticleWorkspace::removeNode(CNode *ptr)
|
|
{
|
|
sint index = getIndexFromNode(ptr);
|
|
nlassert(index != -1);
|
|
removeNode((uint) index);
|
|
}
|
|
|
|
//***********************************************************************************************
|
|
void CParticleWorkspace::save() throw(NLMISC::EStream)
|
|
{
|
|
NLMISC::COFile stream;
|
|
stream.open(_Filename);
|
|
NLMISC::COXml xmlStream;
|
|
xmlStream.init(&stream);
|
|
this->serial(xmlStream);
|
|
clearModifiedFlag();
|
|
}
|
|
|
|
//***********************************************************************************************
|
|
void CParticleWorkspace::load()
|
|
{
|
|
NLMISC::CIFile stream;
|
|
stream.open(_Filename);
|
|
NLMISC::CIXml xmlStream;
|
|
xmlStream.init(stream);
|
|
this->serial(xmlStream);
|
|
clearModifiedFlag();
|
|
}
|
|
|
|
//***********************************************************************************************
|
|
void CParticleWorkspace::serial(NLMISC::IStream &f) throw(NLMISC::EStream)
|
|
{
|
|
f.xmlPush("PARTICLE_WORKSPACE");
|
|
f.serialVersion(0);
|
|
f.xmlSerial(_Name, "NAME");
|
|
f.xmlPush("PS_LIST");
|
|
uint32 numNodes = (uint32)_Nodes.size();
|
|
// TODO : avoid to store the number of nodes
|
|
f.xmlSerial(numNodes, "NUM_NODES");
|
|
if (f.isReading())
|
|
{
|
|
for(uint k = 0; k < numNodes; ++k)
|
|
{
|
|
_Nodes.push_back(new CNode());
|
|
_Nodes.back()->init(this);
|
|
f.serial(*_Nodes.back());
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for(uint k = 0; k < numNodes; ++k)
|
|
{
|
|
f.serial(*_Nodes[k]);
|
|
}
|
|
}
|
|
f.xmlPop();
|
|
f.xmlPop();
|
|
}
|
|
|
|
//***********************************************************************************************
|
|
std::string CParticleWorkspace::getPath() const
|
|
{
|
|
return NLMISC::CPath::standardizePath(NLMISC::CFile::getPath(_Filename));
|
|
}
|
|
|
|
//***********************************************************************************************
|
|
sint CParticleWorkspace::getIndexFromNode(CNode *node) const
|
|
{
|
|
nlassert(node);
|
|
nlassert(node->getWorkspace() == this);
|
|
for(uint k = 0; k < _Nodes.size(); ++k)
|
|
{
|
|
if (node == _Nodes[k]) return (sint) k;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
//***********************************************************************************************
|
|
bool CParticleWorkspace::containsFile(std::string filename) const
|
|
{
|
|
for(uint k = 0; k < _Nodes.size(); ++k)
|
|
{
|
|
if (NLMISC::nlstricmp(filename, _Nodes[k]->getFilename()) == 0) return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
//***********************************************************************************************
|
|
void CParticleWorkspace::nodeModified(CNode &node)
|
|
{
|
|
nlassert(node.getWorkspace() == this);
|
|
if (_ModificationCallback)
|
|
{
|
|
_ModificationCallback->nodeModifiedFlagChanged(node);
|
|
}
|
|
}
|
|
|
|
//***********************************************************************************************
|
|
CParticleWorkspace::CNode *CParticleWorkspace::getNodeFromPS(NL3D::CParticleSystem *ps) const
|
|
{
|
|
for(uint k = 0; k < _Nodes.size(); ++k)
|
|
{
|
|
if (_Nodes[k]->getPSPointer() == ps) return _Nodes[k];
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
//***********************************************************************************************
|
|
void CParticleWorkspace::setModifiedFlag(bool modified)
|
|
{
|
|
if (_Modified == modified) return;
|
|
_Modified = modified;
|
|
if (_ModificationCallback) _ModificationCallback->workspaceModifiedFlagChanged(*this);
|
|
}
|
|
|
|
//***********************************************************************************************
|
|
// predicate for workspace sorting
|
|
class CParticleWorkspaceSorter
|
|
{
|
|
public:
|
|
CParticleWorkspace::ISort *Sorter;
|
|
bool operator()(const NLMISC::CSmartPtr<CParticleWorkspace::CNode> &lhs, const NLMISC::CSmartPtr<CParticleWorkspace::CNode> &rhs)
|
|
{
|
|
return Sorter->less(*lhs, *rhs);
|
|
}
|
|
};
|
|
|
|
//***********************************************************************************************
|
|
void CParticleWorkspace::sort(ISort &predicate)
|
|
{
|
|
CParticleWorkspaceSorter ws;
|
|
ws.Sorter = &predicate;
|
|
std::sort(_Nodes.begin(), _Nodes.end(), ws);
|
|
setModifiedFlag(true);
|
|
}
|
|
|
|
//***********************************************************************************************
|
|
bool CParticleWorkspace::isContentModified() const
|
|
{
|
|
for(uint k = 0; k < _Nodes.size(); ++k)
|
|
{
|
|
if (_Nodes[k]->isModified()) return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
//***********************************************************************************************
|
|
void CParticleWorkspace::restickAllObjects(CObjectViewer *ov)
|
|
{
|
|
for(uint k = 0; k < _Nodes.size(); ++k)
|
|
{
|
|
std::string parentSkelName = _Nodes[k]->getParentSkelName();
|
|
std::string parentBoneName = _Nodes[k]->getParentBoneName();
|
|
//
|
|
_Nodes[k]->unstickPSFromSkeleton();
|
|
if (!parentSkelName.empty())
|
|
// find instance to stick to in the scene
|
|
for(uint l = 0; l < ov->getNumInstance(); ++l)
|
|
{
|
|
CInstanceInfo *ii = ov->getInstance(l);
|
|
if (ii->TransformShape && ii->Saved.ShapeFilename == parentSkelName)
|
|
{
|
|
NL3D::CSkeletonModel *skel = dynamic_cast<NL3D::CSkeletonModel *>(ii->TransformShape);
|
|
if (skel)
|
|
{
|
|
sint boneID = skel->getBoneIdByName(parentBoneName);
|
|
if (boneID != -1)
|
|
{
|
|
_Nodes[k]->stickPSToSkeleton(skel, (uint) boneID, parentSkelName, parentBoneName);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|