1 // Ryzom - MMORPG Framework <http://dev.ryzom.com/projects/ryzom/>
2 // Copyright (C) 2010 Winch Gate Property Limited
4 // This source file has been modified by the following contributors:
5 // Copyright (C) 2020 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/>.
29 #include "entity_animation_manager.h"
30 #include "animation_misc.h"
31 #include "animation_set.h"
32 #include "debug_client.h"
33 #include "sheet_manager.h"
34 #include "time_client.h"
37 #include "nel/misc/path.h"
38 #include "nel/misc/smart_ptr.h"
39 #include "nel/misc/file.h"
40 #include "nel/misc/progress_callback.h"
42 #include "nel/3d/u_skeleton.h"
43 #include "nel/3d/u_driver.h"
44 #include "nel/3d/u_scene.h"
45 #include "nel/3d/u_track.h"
46 #include "nel/3d/u_play_list.h"
47 #include "nel/3d/u_play_list_manager.h"
48 #include "nel/3d/u_animation_set.h"
53 using namespace NLMISC
;
55 using namespace NLGEORGES
;
61 extern NL3D::UScene
* Scene
;
68 H_AUTO_DECL ( RZ_Client_Entity_Animation_Mngr
)
73 CEntityAnimationManager
*CEntityAnimationManager::_Instance
= 0;
74 NL3D::UPlayListManager
*CEntityAnimationManager::_PlayListManager
= 0;
80 //---------------------------------------------------
82 // Instanciate CEntityAnimationManager. There can be only one instance (singleton)
83 // \return CEntityAnimationManager * : Pointer on CEntityAnimationManager.
84 //---------------------------------------------------
85 CEntityAnimationManager
* CEntityAnimationManager::getInstance()
89 _Instance
= new CEntityAnimationManager();
91 _PlayListManager
= Scene
->createPlayListManager();
93 nlwarning("CEntityAnimationManager::getInstance : Scene is not allocated.");
98 //-----------------------------------------------
100 // Release the current instance
101 // \warning If you the kept the pointer given by getInstance, it will be invalid.
102 //-----------------------------------------------
103 void CEntityAnimationManager::delInstance()
105 // Release the singleton
117 //---------------------------------------------------
118 // CEntityAnimationManager :
120 //---------------------------------------------------
121 CEntityAnimationManager::CEntityAnimationManager()
123 _AnimationSet
= NULL
;
124 _AutomatonList
= NULL
;
127 }// CEntityAnimationManager //
129 //---------------------------------------------------
130 // ~CEntityAnimationManager :
132 //---------------------------------------------------
133 CEntityAnimationManager::~CEntityAnimationManager()
135 // Delete the playlist manager
139 // Scene->deletePlayListManager(_PlayListManager);
140 _PlayListManager
= 0;
143 // Release all things initialized by the load method.
145 }// ~CEntityAnimationManager //
147 //-----------------------------------------------
149 // Release all things initialized by the load method.
150 //-----------------------------------------------
151 void CEntityAnimationManager::release()
153 // Animation set cache
154 _AnimationSetPosCache
.clear();
155 _AnimationSetRotCache
.clear();
158 // Release Automatons and Release the emots list
159 // Nothing to release here : all is in the sheet manager so kept during all the time the game is running
160 // Release the AnimationSet.
163 Driver
->deleteAnimationSet(_AnimationSet
);
167 // owned by CSheetManager
168 _AutomatonList
= NULL
;
172 //---------------------------------------------------
174 //---------------------------------------------------
175 void CEntityAnimationManager::load(NLMISC::IProgressCallback
&/* progress */, bool /* forceRepack */ /* = false */)
177 // Whole initInGame profile
178 NLMISC::TTime initStart
;
179 initStart
= ryzomGetLocalTime ();
181 // Before loading, release the old stuff.
184 // Log in the animation file.
185 //setDebugOutput(getLogDirectory() + "animation.dbg");
186 setDebugOutput(""); // no log
188 // Create the animation set for all entities.
189 _AnimationSet
= Driver
->createAnimationSet();
190 if(_AnimationSet
== 0)
191 pushDebugStr("_AnimationSet is Null !.");
194 pushInfoStr("COMPUTING EMOTS ...");
195 // Initialize the emot list
196 _EmotList
= dynamic_cast<CEmotListSheet
*>(SheetMngr
.get(CSheetId("list.emot")));
197 nlassert(_EmotList
!= NULL
);
199 pushInfoStr("COMPUTING AUTOMATON ...");
200 // Initialize the automaton.
201 _AutomatonList
= dynamic_cast<CAutomatonListSheet
*>(SheetMngr
.get(CSheetId("automaton.automaton_list")));
202 nlassert(_AutomatonList
!= NULL
);
204 pushInfoStr("COMPUTING ANIMATION SET ...");
205 // Initialize the list of animsets
206 CAnimationSetListSheet
*pASLS
= dynamic_cast<CAnimationSetListSheet
*>(SheetMngr
.get(CSheetId("entities.animset_list")));
207 nlassert(pASLS
!= NULL
);
208 for (uint32 i
= 0; i
< pASLS
->AnimSetList
.size(); ++i
)
211 string sTmp
= toLowerAscii(pASLS
->AnimSetList
[i
].Name
);
212 sTmp
= sTmp
.substr(0,sTmp
.rfind('.'));
213 pair
<map
<string
,CAnimationSet
>::iterator
, bool> it
;
214 it
= _AnimSet
.insert(pair
<string
,CAnimationSet
>(sTmp
,as
));
215 it
.first
->second
.init (&pASLS
->AnimSetList
[i
], _AnimationSet
);
218 // Build 3d Animation Set (called after all animations added in the animation set).
219 _AnimationSetPosCache
.resize (0, NULL
);
220 _AnimationSetRotCache
.resize (0, NULL
);
223 _AnimationSet
->build();
225 // Build animation set cache
227 const uint count
= _AnimationSet
->getNumAnimation ();
228 _AnimationSetPosCache
.resize (count
, NULL
);
229 _AnimationSetRotCache
.resize (count
, NULL
);
230 for (i
=0; i
<count
; i
++)
233 NL3D::UAnimation
*anim
= _AnimationSet
->getAnimation (i
);
237 _AnimationSetPosCache
[i
] = anim
->getTrackByName("pos");
238 _AnimationSetRotCache
[i
] = anim
->getTrackByName("rotquat");
243 // Flush debug information
244 flushDebugStack("-- ANIMATIONS OTHER --");
246 // Stop to Log in the animation file.
249 // Whole load profile
250 nlinfo ("%d seconds for EAM->load()", (uint32
)(ryzomGetLocalTime ()-initStart
)/1000);
253 uint32 nTotalAnimSet = _AnimSet.size();
254 TAnimSet::iterator it = _AnimSet.begin();
255 uint32 nTotalAnimState = 0;
256 uint32 nTotalAnim = 0;
257 uint32 nTotalAnimFx = 0;
259 while (it != _AnimSet.end())
261 CAnimationSet &rAS = it->second;
262 uint32 nAS = rAS.getNumAnimationState();
263 nTotalAnimState += nAS;
264 for (uint i = 0; i < nAS; ++i)
266 CAnimationState *pAnimState = rAS.getAnimationStateByIndex(i);
267 uint32 nAnim = pAnimState->getNumAnimation();
269 for (uint j = 0; j < nAnim; ++j)
271 CAnimation *pAnim = pAnimState->getAnimationByIndex(j);
272 nTotalAnimFx += pAnim->getFXSet().FX.size();
276 nlinfo ("%d %d %d", sizeof(rAS), sizeof(*pAnimState), sizeof(*pAnim));
284 nlinfo ("%d %d %d", nTotalAnimSet, nTotalAnimState, nTotalAnim, nTotalAnimFx);
289 //---------------------------------------------------
291 // Animate all the playlists.
292 // \param double time : play time.
293 //---------------------------------------------------
294 void CEntityAnimationManager::animate(double time
)
296 H_AUTO_USE ( RZ_Client_Entity_Animation_Mngr
)
298 _PlayListManager
->animate(time
);
302 //---------------------------------------------------
304 // Setup all the playlists.
305 // \param double time : play time.
306 //---------------------------------------------------
307 void CEntityAnimationManager::setup(double time
)
309 H_AUTO_USE ( RZ_Client_Entity_Animation_Mngr
)
311 if (_PlayListManager
) _PlayListManager
->setup(time
);
315 //---------------------------------------------------
317 // Return a ref on a state of the moving automaton defined by its key.
318 // \param string automatonName : the automaton's name.
319 // \param TAnimStateKey key : the key of the state.
320 // \return CMovingAutomatonState * : pointer .
321 //---------------------------------------------------
322 const CAutomatonStateSheet
*CEntityAnimationManager::mState(const string
&automaton
, TAnimStateKey key
)
324 // Search for the automaton
325 std::map
<std::string
, CAutomatonSheet
*>::iterator it
= _AutomatonList
->Automatons
.find(automaton
);
326 if(it
!= _AutomatonList
->Automatons
.end())
329 const CAutomatonStateSheet
*statePtr
= ((*it
).second
)->state(key
);
332 nlwarning("CEntityAnimationManager::mState: the state '%s' does not exist for the automaton '%s'", CAnimationState::getAnimationStateName(key
).c_str(), automaton
.c_str());
338 // Automaton not found
343 //---------------------------------------------------
345 // Delete a play list.
346 // \param UPlayList * pl : pointer on the play list to delete.
347 //---------------------------------------------------
348 void CEntityAnimationManager::deletePlayList(NL3D::UPlayList
* pl
)
350 // Check if the playlist is not Null.
353 _PlayListManager
->deletePlayList(pl
);
356 }// deletePlayList //
359 //---------------------------------------------------
362 //---------------------------------------------------
363 void CEntityAnimationManager::processLogic(const std::string &animListName, TAnimStateKey animationId, CAnimationTime startTimeOffset, CAnimationTime endTimeOffset, UTrack *&soundTrack, vector<CAnimationTime>& result)
367 // Get the animation set according to the animation list name
368 UAnimationSet *animSet = getAnimationSet(animListName);
371 // Get the animation Id.
372 uint idAnim = animSet->getAnimationIdByName(animationId);
373 if(idAnim != UAnimationSet::NotFound)
375 // Get the animation pointer and get the length.
376 UAnimation *anim = animSet->getAnimation(idAnim);
379 char *soundTrackName = "NoteTrack";
380 soundTrack = anim->getTrackByName( soundTrackName );
381 if( soundTrack != NULL )
383 UTrackKeyframer * soundTrackKF = dynamic_cast<UTrackKeyframer *>(soundTrack);
384 if( soundTrackKF == NULL )
386 nlerror("The track %s is not a key framer track",soundTrackName);
389 soundTrackKF->getKeysInRange(startTimeOffset, endTimeOffset, result);
402 //---------------------------------------------------
403 // getAnimationLength :
404 // Return an animation length (in sec).
405 // \param string animName : Animation Name.
406 // \return double : the length of the animation.
407 // \warning This method never return a value <= 0.0 and so will return 1.0 instead.
408 // \warning This Method is slower than the one with the animation Id instead of the animation Name.
409 //---------------------------------------------------
410 double CEntityAnimationManager::getAnimationLength(const std::string
&animName
) const
412 double length
= CAnimationMisc::getAnimationLength(_AnimationSet
, animName
);
417 }// getAnimationLength //
419 //---------------------------------------------------
420 // getAnimationLength :
421 // Return an animation length (in sec).
422 // \param idAnim : id of the animation.
423 // \return double : the length of the animation.
424 // \warning This method never return a value <= 0.0 and so will return 1.0 instead.
425 // \warning This Method is slower than the one with the animation Id instead of the animation Name.
426 //---------------------------------------------------
427 double CEntityAnimationManager::getAnimationLength(uint idAnim
) const
429 if (idAnim
== NL3D::UAnimationSet::NotFound
) return 0.f
;
430 double length
= CAnimationMisc::getAnimationLength(_AnimationSet
, idAnim
);
435 }// getAnimationLength //
438 //---------------------------------------------------
439 // getAnimationAverageSpeed :
440 // Get the average speed of an animation (in meters/sec).
441 // \param string animName : Animation Name.
442 // \return double : the average speed (in m/s).
443 //---------------------------------------------------
444 double CEntityAnimationManager::getAnimationAverageSpeed(const std::string
&animName
) const
446 return CAnimationMisc::getAnimationAverageSpeed(_AnimationSet
, animName
);
447 }// getAnimationAverageSpeed //
449 //---------------------------------------------------
450 // getAnimationAverageSpeed :
451 // Get the average speed of an animation (in meters/sec).
452 // \param string animName : Animation Name.
453 // \return double : the average speed (in m/s).
454 //---------------------------------------------------
455 double CEntityAnimationManager::getAnimationAverageSpeed(uint idAnim
) const
457 return CAnimationMisc::getAnimationAverageSpeed(_AnimationSet
, idAnim
);
458 }// getAnimationAverageSpeed //
461 //---------------------------------------------------
463 // Return a pointer on the set according to the set name.
464 // \param animSet : name of the set.
465 // \return CAnimationSet * : pointer of the right Set or 0.
466 //---------------------------------------------------
467 const CAnimationSet
*CEntityAnimationManager::getAnimSet(const std::string
&animSet
) const
469 // Search the right animation set.
470 TAnimSet::const_iterator itAnimSet
= _AnimSet
.find(animSet
);
471 if(itAnimSet
== _AnimSet
.end())
475 // Return the pointer
476 return &((*itAnimSet
).second
);
480 //---------------------------------------------------
482 // Create a playlist.
483 // \return UPlayList * : a pointer on a play list or 0 if any pb.
484 //---------------------------------------------------
485 NL3D::UPlayList
*CEntityAnimationManager::createPlayList() const
487 if (_PlayListManager
== 0)
490 return _PlayListManager
->createPlayList(_AnimationSet
);
491 }// createPlayList //
495 //-----------------------------------------------
497 // Serialize a CEntityAnimationManager.
498 //-----------------------------------------------
499 void CEntityAnimationManager::serial(NLMISC::IStream
&/* f */)
504 // ***************************************************************************
505 void CEntityAnimationManager::resetAllSoundAnimId()
507 TAnimSet::iterator
it(_AnimSet
.begin()), last(_AnimSet
.end());
510 CAnimationSet
&animSet
= it
->second
;
511 uint numAnimState
= animSet
.getNumAnimationState();
512 for(uint i
=0;i
<numAnimState
;i
++)
514 CAnimationState
*state
= animSet
.getAnimationStateByIndex(i
);
517 uint numAnim
= state
->getNumAnimation();
518 for(uint j
=0;j
<numAnim
;j
++)
520 CAnimation
*anim
= state
->getAnimationByIndex(j
);
522 anim
->resetSoundAnim();
529 // ***************************************************************************
530 void CEntityAnimationManager::reloadAllSoundAnim()
532 TAnimSet::iterator
it(_AnimSet
.begin()), last(_AnimSet
.end());
535 CAnimationSet
&animSet
= it
->second
;
536 uint numAnimState
= animSet
.getNumAnimationState();
537 for(uint i
=0;i
<numAnimState
;i
++)
539 CAnimationState
*state
= animSet
.getAnimationStateByIndex(i
);
542 uint numAnim
= state
->getNumAnimation();
543 for(uint j
=0;j
<numAnim
;j
++)
545 CAnimation
*anim
= state
->getAnimationByIndex(j
);
547 anim
->reloadSoundAnim();
556 // \todo GUIGUI : A REFAIRE.
558 //---------------------------------------------------
559 // createFacePlayList :
560 // Create an uninitialized playlist for the face animations.
561 // \param type : type of the play list to create.
562 // \return UPlayList * : a pointer on an initialized play list.
563 //---------------------------------------------------
564 UPlayList * CEntityAnimationManager::createFacePlayList(const CTypeEntity& type)
566 // get the animation set
567 UAnimationSet *animationSet = getFaceAnimationSet(type);
569 return _PlayListManager->createPlayList(animationSet);
572 }// createFacePlayList //
574 //---------------------------------------------------
575 // createFaceAnimationSet :
576 // Create an animation set for the Face.
577 //---------------------------------------------------
578 void CEntityAnimationManager::createFaceAnimationSet(const CTypeEntity& type)
580 UAnimationSet * faceAnimationSet = Driver->createAnimationSet();
581 if(faceAnimationSet == 0)
582 nlwarning("CEntityAnimationManager::createFaceAnimationSet : cannot create an animation set for the Face.");
583 // Insert the new animation set in the map
585 _FaceAnimationSets.insert(make_pair(type, faceAnimationSet));
586 }// createFaceAnimationSet //
589 //---------------------------------------------------
590 // getFaceAnimationSet :
591 // Get an animation set for face.
592 // \param type : type of the animation set you want.
593 // \return UAnimationSet * : a pointer on the animation set or 0 if there is not animation set for this type.
594 //---------------------------------------------------
595 UAnimationSet * CEntityAnimationManager::getFaceAnimationSet(const CTypeEntity& type)
597 map<CTypeEntity, UAnimationSet *>::iterator itAnimSet = _FaceAnimationSets.find(type);
598 if(itAnimSet == _FaceAnimationSets.end())
601 return (*itAnimSet).second;
602 }// getFaceAnimationSet //
604 //---------------------------------------------------
606 // Choose an animation for the face according to the type and the emotion.
607 // \param type : type of the face.
608 // \param emotion : emotion to play.
609 // \return uint : the index of the animation.
610 // \todo GUIGUI : make a real choice according to the percentage.
611 //---------------------------------------------------
612 uint CEntityAnimationManager::chooseFaceAnim(const CTypeEntity& type, const string &emotion)
614 std::map<CTypeEntity, TFaceEmotions>::iterator itFEPT = _FaceEmotionsPerType.find(type);
615 if(itFEPT == _FaceEmotionsPerType.end())
617 nlwarning("CEntityAnimationManager::chooseFaceAnim : Nothing associated to the type.");
618 return UAnimationSet::NotFound;
621 TFaceEmotions &faceEmotions = (*itFEPT).second;
622 TFaceEmotions::iterator itFE = faceEmotions.find(emotion);
623 if(itFE == faceEmotions.end())
625 nlwarning("CEntityAnimationManager::chooseFaceAnim : Face Emotion '%s' does not exist for this type.", emotion.c_str());
626 return UAnimationSet::NotFound;
629 CFaceEmotion &faceEmotion = (*itFE).second;
630 if(faceEmotion._Anims.empty())
632 nlwarning("No animation in the Emotion '%s'.", emotion.c_str());
633 return UAnimationSet::NotFound;
636 string anim = (*faceEmotion._Anims.begin()).first;
637 UAnimationSet *animSet = getFaceAnimationSet(type);
639 return animSet->getAnimationIdByName(anim);
641 return UAnimationSet::NotFound;
642 }// chooseFaceAnim //
644 //-----------------------------------------------
645 // loadFaceAnimations :
646 // Load table containing infos about face animations.
647 // \param type : table is different according to the type.
648 // \param fileName : the name of the file containing the animation infos.
649 // \todo GUIGUI : Check that we can add the same animation more than once with no problem or do i have to check if the animation is already in.
650 //-----------------------------------------------
651 void CEntityAnimationManager::loadFaceAnimations(const CTypeEntity& type, const char * fileName)
653 // Get the animation set.
654 UAnimationSet * animationSet = getFaceAnimationSet(type);
657 nlwarning("CEntityAnimationManager::loadFaceAnimations : animationSet is Null for the type -> Face Animations not loaded.");
661 // Create Face Emotions struct.
662 TFaceEmotions faceEmotions;
668 map<string, CSTLoader::TDataType> fileFormat;
669 fileFormat.insert(make_pair(string("name"), CSTLoader::STRING));
670 fileFormat.insert(make_pair(string("filename"), CSTLoader::STRING));
671 fileFormat.insert(make_pair(string("percentage"), CSTLoader::FLOAT));
674 cstl.init(fileName, fileFormat);
675 // Read the file line per line.
676 while(cstl.readLine())
678 // Get the name of the emotion.
682 cstl.getStringValue ("name", emotionName);
683 cstl.getStringValue ("filename", animFilename);
684 cstl.getValue ("percentage", percentage);
686 // Try to add the anim in the animation set.
687 if(animationSet->addAnimation((animFilename + ".anim").c_str(), animFilename.c_str()) == UAnimationSet::NotFound)
688 nlwarning("CEntityAnimationManager::loadFaceAnimations : anim '%s' is not found.", (animFilename + ".anim").c_str());
689 // The animation is right added in the set -> add anim in the right face emotion.
692 // Attempt on find this name in the map.
693 map<string, CFaceEmotion>::iterator itFaceEmotion = faceEmotions.find(emotionName);
694 // This emotion already exist -> add new animation to the emotion.
695 if(itFaceEmotion != faceEmotions.end())
697 CFaceEmotion &faceEmotion = (*itFaceEmotion).second;
698 faceEmotion.addAnim(animFilename, percentage);
702 CFaceEmotion faceEmotion;
703 faceEmotion.addAnim(animFilename, percentage);
704 faceEmotions.insert(make_pair(emotionName, faceEmotion));
712 // Add Emotions for the type.
713 _FaceEmotionsPerType.insert(make_pair(type, faceEmotions));
714 }// loadFaceAnimations //