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/>.
23 #include "particle_workspace.h"
24 #include "object_viewer.h"
26 #include "nel/3d/shape_bank.h"
27 #include "nel/3d/particle_system_model.h"
28 #include "nel/3d/particle_system_shape.h"
29 #include "nel/3d/skeleton_model.h"
31 #include "nel/misc/o_xml.h"
32 #include "nel/misc/i_xml.h"
33 #include "nel/misc/file.h"
38 //***********************************************************************************************
39 CParticleWorkspace::CNode::CNode()
46 _ResetAutoCount
= false;
50 //***********************************************************************************************
51 CParticleWorkspace::CNode::~CNode()
57 //***********************************************************************************************
58 void CParticleWorkspace::CNode::memorizeState()
62 _InitialPos
.copySystemInitialPos(_PS
);
65 //***********************************************************************************************
66 void CParticleWorkspace::CNode::restoreState()
70 _InitialPos
.restoreSystem();
73 //**************************************************************************************************************************
74 bool CParticleWorkspace::CNode::isStateMemorized() const
76 return _InitialPos
.isStateMemorized();
79 //**************************************************************************************************************************
80 void CParticleWorkspace::CNode::stickPSToSkeleton(NL3D::CSkeletonModel
*skel
,
82 const std::string
&parentSkelName
,
83 const std::string
&parentBoneName
)
87 unstickPSFromSkeleton();
88 _ParentSkelName
= parentSkelName
;
89 _ParentBoneName
= parentBoneName
;
92 skel
->stickObject(_PSM
, bone
);
93 _PSM
->setMatrix(NLMISC::CMatrix::Identity
);
96 if (_WS
->getModificationCallback())
98 _WS
->getModificationCallback()->nodeSkelParentChanged(*this);
102 //**************************************************************************************************************************
103 void CParticleWorkspace::CNode::unstickPSFromSkeleton()
106 _ParentSkelName
.clear();
107 _ParentBoneName
.clear();
111 _ParentSkel
->detachSkeletonSon(_PSM
);
116 //***********************************************************************************************
117 void CParticleWorkspace::CNode::removeLocated(NL3D::CPSLocated
*loc
)
120 if (_InitialPos
.isStateMemorized())
122 _InitialPos
.removeLocated(loc
);
126 //***********************************************************************************************
127 void CParticleWorkspace::CNode::removeLocatedBindable(NL3D::CPSLocatedBindable
*lb
)
130 if (_InitialPos
.isStateMemorized())
132 _InitialPos
.removeLocatedBindable(lb
);
136 //***********************************************************************************************
137 void CParticleWorkspace::CNode::setModified(bool modified
)
140 if (_Modified
== modified
) return;
141 _Modified
= modified
;
142 _WS
->nodeModified(*this);
145 //***********************************************************************************************
146 void CParticleWorkspace::CNode::unload()
151 NL3D::CShapeBank
*oldSB
= NL3D::CNELU::Scene
->getShapeBank();
152 NL3D::CNELU::Scene
->setShapeBank(_ShapeBank
);
153 NL3D::CNELU::Scene
->deleteInstance(_PSM
);
154 NL3D::CNELU::Scene
->setShapeBank(oldSB
);
164 //***********************************************************************************************
165 void CParticleWorkspace::CNode::setup(NL3D::CParticleSystemModel
&psm
)
168 psm
.setTransformMode(NL3D::CTransform::DirectMatrix
);
169 psm
.setMatrix(NLMISC::CMatrix::Identity
);
170 nlassert(_WS
->getObjectViewer());
171 psm
.setEditionMode(true); // this also force the system instanciation
172 for(uint k
= 0; k
< NL3D::MaxPSUserParam
; ++k
)
174 psm
.bypassGlobalUserParamValue(k
);
176 psm
.enableAutoGetEllapsedTime(false);
177 psm
.setEllapsedTime(0.f
); // system is paused
178 // initialy, the ps is hidden
180 // link to the root for manipulation
181 _WS
->getObjectViewer()->getSceneRoot()->hrcLinkSon(&psm
);
182 NL3D::CParticleSystem
*ps
= psm
.getPS();
184 ps
->setFontManager(_WS
->getFontManager());
185 ps
->setFontGenerator(_WS
->getFontGenerator());
188 psm
.Shape
->flushTextures(*NL3D::CNELU::Driver
, 0);
191 //***********************************************************************************************
192 void CParticleWorkspace::CNode::setTriggerAnim(const std::string
&anim
)
195 if (anim
== _TriggerAnim
) return;
201 //***********************************************************************************************
202 void CParticleWorkspace::CNode::createEmptyPS()
205 NL3D::CParticleSystem emptyPS
;
206 NL3D::CParticleSystemShape
*pss
= new NL3D::CParticleSystemShape
;
207 pss
->buildFromPS(emptyPS
);
208 CUniquePtr
<NL3D::CShapeBank
> sb(new NL3D::CShapeBank
);
209 std::string shapeName
= NLMISC::CFile::getFilename(_RelativePath
);
210 sb
->add(shapeName
, pss
);
211 NL3D::CShapeBank
*oldSB
= NL3D::CNELU::Scene
->getShapeBank();
212 NL3D::CNELU::Scene
->setShapeBank(sb
.get());
213 NL3D::CParticleSystemModel
*psm
= NLMISC::safe_cast
<NL3D::CParticleSystemModel
*>(NL3D::CNELU::Scene
->createInstance(shapeName
));
215 NL3D::CNELU::Scene
->setShapeBank(oldSB
);
221 _ShapeBank
= sb
.release();
225 //***********************************************************************************************
226 void CParticleWorkspace::CNode::init(CParticleWorkspace
*ws
)
232 //***********************************************************************************************
233 void CParticleWorkspace::CNode::setRelativePath(const std::string
&relativePath
)
236 _RelativePath
= relativePath
;
239 //***********************************************************************************************
240 void CParticleWorkspace::CNode::serial(NLMISC::IStream
&f
)
243 f
.xmlPush("PROJECT_FILE");
244 sint version
= f
.serialVersion(2);
245 f
.xmlSerial(_RelativePath
, "RELATIVE_PATH");
248 f
.xmlSerial(_TriggerAnim
, "TRIGGER_ANIMATION");
252 f
.xmlSerial(_ParentSkelName
, "PARENT_SKEL_NAME");
253 f
.xmlSerial(_ParentBoneName
, "PARENT_BONE_NAME");
258 //***********************************************************************************************
259 void CParticleWorkspace::CNode::savePS()
261 savePSAs(getFullPath());
265 //***********************************************************************************************
266 void CParticleWorkspace::CNode::savePSAs(const std::string
&fullPath
)
270 // build a shape from our system, and save it
271 NL3D::CParticleSystemShape psc
;
272 psc
.buildFromPS(*_PS
);
273 NL3D::CShapeStream
st(&psc
);
274 NLMISC::COFile
oFile(fullPath
);
278 //***********************************************************************************************
279 std::string
CParticleWorkspace::CNode::getFullPath() const
282 return NLMISC::CPath::makePathAbsolute(_RelativePath
, _WS
->getPath(), true);
285 //***********************************************************************************************
286 bool CParticleWorkspace::CNode::loadPS()
289 // manually load the PS shape (so that we can deal with exceptions)
290 NL3D::CShapeStream ss
;
291 NLMISC::CIFile inputFile
;
293 inputFile
.open(getFullPath());
294 ss
.serial(inputFile
);
295 CUniquePtr
<NL3D::CShapeBank
> sb(new NL3D::CShapeBank
);
296 std::string shapeName
= NLMISC::CFile::getFilename(_RelativePath
);
297 sb
->add(shapeName
, ss
.getShapePointer());
298 NL3D::CShapeBank
*oldSB
= NL3D::CNELU::Scene
->getShapeBank();
299 NL3D::CNELU::Scene
->setShapeBank(sb
.get());
300 NL3D::CTransformShape
*trs
= NL3D::CNELU::Scene
->createInstance(shapeName
);
303 NL3D::CNELU::Scene
->setShapeBank(oldSB
);
306 NL3D::CParticleSystemModel
*psm
= dynamic_cast<NL3D::CParticleSystemModel
*>(trs
);
309 // Not a particle system
310 NL3D::CNELU::Scene
->deleteInstance(trs
);
313 NL3D::CNELU::Scene
->setShapeBank(oldSB
);
319 _ShapeBank
= sb
.release();
325 //***********************************************************************************************
326 CParticleWorkspace::CParticleWorkspace()
331 _FontGenerator
= NULL
;
332 _ModificationCallback
= NULL
;
335 //***********************************************************************************************
336 CParticleWorkspace::~CParticleWorkspace()
340 //***********************************************************************************************
341 void CParticleWorkspace::init(CObjectViewer
*ov
,
342 const std::string
&filename
,
343 NL3D::CFontManager
*fontManager
,
344 NL3D::CFontGenerator
*fontGenerator
350 _Filename
= filename
;
351 _FontManager
= fontManager
;
352 _FontGenerator
= fontGenerator
;
354 //***********************************************************************************************
355 void CParticleWorkspace::setName(const std::string
&name
)
362 //***********************************************************************************************
363 CParticleWorkspace::CNode
*CParticleWorkspace::addNode(const std::string
&filenameWithFullPath
)
366 // Check that file is not already inserted
367 std::string fileName
= NLMISC::CFile::getFilename(filenameWithFullPath
);
368 for(uint k
= 0; k
< _Nodes
.size(); ++k
)
370 if (NLMISC::nlstricmp(_Nodes
[k
]->getFilename(), fileName
) == 0) return NULL
;
373 // TODO: replace with NeL methods
374 TCHAR resultPath
[MAX_PATH
];
375 std::string dosPath
= NLMISC::CPath::standardizeDosPath(getPath());
376 std::string relativePath
;
377 if (!PathRelativePathTo(resultPath
, nlUtf8ToTStr(dosPath
), FILE_ATTRIBUTE_DIRECTORY
, nlUtf8ToTStr(filenameWithFullPath
), 0))
379 relativePath
= filenameWithFullPath
;
383 relativePath
= NLMISC::tStrToUtf8(resultPath
);
386 if (relativePath
.size() >= 2)
388 if (relativePath
[0] == '\\' && relativePath
[1] != '\\')
390 relativePath
= relativePath
.substr(1);
394 CNode
*newNode
= new CNode
;
396 newNode
->setRelativePath(relativePath
);
397 _Nodes
.push_back(newNode
);
398 setModifiedFlag(true);
402 //***********************************************************************************************
403 void CParticleWorkspace::removeNode(uint index
)
406 nlassert(index
< _Nodes
.size());
407 _Nodes
[index
] = NULL
; // delete the smart-ptr target
408 _Nodes
.erase(_Nodes
.begin() + index
);
412 //***********************************************************************************************
413 void CParticleWorkspace::removeNode(CNode
*ptr
)
415 sint index
= getIndexFromNode(ptr
);
416 nlassert(index
!= -1);
417 removeNode((uint
) index
);
420 //***********************************************************************************************
421 void CParticleWorkspace::save()
423 NLMISC::COFile stream
;
424 stream
.open(_Filename
);
425 NLMISC::COXml xmlStream
;
426 xmlStream
.init(&stream
);
427 this->serial(xmlStream
);
431 //***********************************************************************************************
432 void CParticleWorkspace::load()
434 NLMISC::CIFile stream
;
435 stream
.open(_Filename
);
436 NLMISC::CIXml xmlStream
;
437 xmlStream
.init(stream
);
438 this->serial(xmlStream
);
442 //***********************************************************************************************
443 void CParticleWorkspace::serial(NLMISC::IStream
&f
)
445 f
.xmlPush("PARTICLE_WORKSPACE");
447 f
.xmlSerial(_Name
, "NAME");
448 f
.xmlPush("PS_LIST");
449 uint32 numNodes
= (uint32
)_Nodes
.size();
450 // TODO : avoid to store the number of nodes
451 f
.xmlSerial(numNodes
, "NUM_NODES");
454 for(uint k
= 0; k
< numNodes
; ++k
)
456 _Nodes
.push_back(new CNode());
457 _Nodes
.back()->init(this);
458 f
.serial(*_Nodes
.back());
463 for(uint k
= 0; k
< numNodes
; ++k
)
465 f
.serial(*_Nodes
[k
]);
472 //***********************************************************************************************
473 std::string
CParticleWorkspace::getPath() const
475 return NLMISC::CPath::standardizePath(NLMISC::CFile::getPath(_Filename
));
478 //***********************************************************************************************
479 sint
CParticleWorkspace::getIndexFromNode(CNode
*node
) const
482 nlassert(node
->getWorkspace() == this);
483 for(uint k
= 0; k
< _Nodes
.size(); ++k
)
485 if (node
== _Nodes
[k
]) return (sint
) k
;
490 //***********************************************************************************************
491 bool CParticleWorkspace::containsFile(std::string filename
) const
493 for(uint k
= 0; k
< _Nodes
.size(); ++k
)
495 if (NLMISC::nlstricmp(filename
, _Nodes
[k
]->getFilename()) == 0) return true;
500 //***********************************************************************************************
501 void CParticleWorkspace::nodeModified(CNode
&node
)
503 nlassert(node
.getWorkspace() == this);
504 if (_ModificationCallback
)
506 _ModificationCallback
->nodeModifiedFlagChanged(node
);
510 //***********************************************************************************************
511 CParticleWorkspace::CNode
*CParticleWorkspace::getNodeFromPS(NL3D::CParticleSystem
*ps
) const
513 for(uint k
= 0; k
< _Nodes
.size(); ++k
)
515 if (_Nodes
[k
]->getPSPointer() == ps
) return _Nodes
[k
];
520 //***********************************************************************************************
521 void CParticleWorkspace::setModifiedFlag(bool modified
)
523 if (_Modified
== modified
) return;
524 _Modified
= modified
;
525 if (_ModificationCallback
) _ModificationCallback
->workspaceModifiedFlagChanged(*this);
528 //***********************************************************************************************
529 // predicate for workspace sorting
530 class CParticleWorkspaceSorter
533 CParticleWorkspace::ISort
*Sorter
;
534 bool operator()(const NLMISC::CSmartPtr
<CParticleWorkspace::CNode
> &lhs
, const NLMISC::CSmartPtr
<CParticleWorkspace::CNode
> &rhs
)
536 return Sorter
->less(*lhs
, *rhs
);
540 //***********************************************************************************************
541 void CParticleWorkspace::sort(ISort
&predicate
)
543 CParticleWorkspaceSorter ws
;
544 ws
.Sorter
= &predicate
;
545 std::sort(_Nodes
.begin(), _Nodes
.end(), ws
);
546 setModifiedFlag(true);
549 //***********************************************************************************************
550 bool CParticleWorkspace::isContentModified() const
552 for(uint k
= 0; k
< _Nodes
.size(); ++k
)
554 if (_Nodes
[k
]->isModified()) return true;
559 //***********************************************************************************************
560 void CParticleWorkspace::restickAllObjects(CObjectViewer
*ov
)
562 for(uint k
= 0; k
< _Nodes
.size(); ++k
)
564 std::string parentSkelName
= _Nodes
[k
]->getParentSkelName();
565 std::string parentBoneName
= _Nodes
[k
]->getParentBoneName();
567 _Nodes
[k
]->unstickPSFromSkeleton();
568 if (!parentSkelName
.empty())
569 // find instance to stick to in the scene
570 for(uint l
= 0; l
< ov
->getNumInstance(); ++l
)
572 CInstanceInfo
*ii
= ov
->getInstance(l
);
573 if (ii
->TransformShape
&& ii
->Saved
.ShapeFilename
== parentSkelName
)
575 NL3D::CSkeletonModel
*skel
= dynamic_cast<NL3D::CSkeletonModel
*>(ii
->TransformShape
);
578 sint boneID
= skel
->getBoneIdByName(parentBoneName
);
581 _Nodes
[k
]->stickPSToSkeleton(skel
, (uint
) boneID
, parentSkelName
, parentBoneName
);