// NeL - MMORPG Framework // 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 . #include "std3d.h" #include "nel/3d/animation.h" #include "nel/3d/animation_set.h" #include "nel/3d/track.h" #include "nel/3d/track_sampled_quat_small_header.h" #include "nel/misc/file.h" #include "nel/misc/path.h" #include "nel/misc/hierarchical_timer.h" #include "nel/misc/algo.h" using namespace std; using namespace NLMISC; #ifdef DEBUG_NEW #define new DEBUG_NEW #endif namespace NL3D { H_AUTO_DECL( NL3D_UI_Animation ) #define NL3D_HAUTO_UI_ANIMATION H_AUTO_USE( NL3D_UI_Animation ) // *************************************************************************** CAnimation::CAnimation() : _BeginTimeTouched(true), _EndTimeTouched(true), _AnimLoopTouched(true) { _MinEndTime = -FLT_MAX; _TrackSamplePack= NULL; _AnimationSetOwner= NULL; } // *************************************************************************** CAnimation::~CAnimation () { // Delete all the pointers in the array for (uint i=0; i<_TrackVector.size(); i++) // Delete delete _TrackVector[i]; // if created, release the _TrackSamplePack if(_TrackSamplePack) delete _TrackSamplePack; _TrackSamplePack= NULL; } // *************************************************************************** void CAnimation::addTrack (const std::string& name, ITrack* pChannel) { // must not already be HeaderOptimized nlassert(_IdByChannelId.empty()); // Add an entry in the map _IdByName.insert (TMapStringUInt::value_type (name, (uint32)_TrackVector.size())); // Add an entry in the array _TrackVector.push_back (pChannel); // _BeginTimeTouched = _EndTimeTouched = _AnimLoopTouched= true; } // *************************************************************************** void CAnimation::serial (NLMISC::IStream& f) { // cannot save if anim header compressed nlassert(_IdByChannelId.empty()); // Serial a header f.serialCheck (NELID("_LEN")); f.serialCheck (NELID("MINA")); // Serial a version sint version=f.serialVersion (2); // Serial the name f.serial (_Name); // Serial the name/id map f.serialCont(_IdByName); // Serial the vector f.serialContPolyPtr (_TrackVector); // Serial the min end time if (version>=1) { f.serial (_MinEndTime); } // Serial the SSS shapes if (version>=2) { f.serialCont (_SSSShapes); } // TestYoyo //nlinfo("ANIMYOYO: Anim NumTracks: %d", _TrackVector.size()); } // *************************************************************************** uint CAnimation::getIdTrackByName (const std::string& name) const { // if not be HeaderOptimized if (_IdByChannelId.empty()) { // Find an entry in the name/id map TMapStringUInt::const_iterator ite=_IdByName.find (name); // Not found ? if (ite==_IdByName.end ()) // yes, error return NotFound; else // no, return track ID return (uint)ite->second; } else { nlassert(_AnimationSetOwner); // get the channel id from name uint channelId= _AnimationSetOwner->getChannelIdByName(name); if(channelId==CAnimationSet::NotFound) return CAnimation::NotFound; else return getIdTrackByChannelId(channelId); } } // *************************************************************************** void CAnimation::getTrackNames (std::set& setString) const { // if not be HeaderOptimized if (_IdByChannelId.empty()) { // For each track name TMapStringUInt::const_iterator ite=_IdByName.begin(); while (ite!=_IdByName.end()) { // Add the name in the map setString.insert (ite->first); // Next track ite++; } } else { nlassert(_AnimationSetOwner); // For each track channel Id, for(uint i=0;i<_IdByChannelId.size();i++) { // Add in the map the channel name => same as track name setString.insert ( _AnimationSetOwner->getChannelName(_IdByChannelId[i]) ); } } } // *************************************************************************** TAnimationTime CAnimation::getBeginTime () const { NL3D_HAUTO_UI_ANIMATION; if (_BeginTimeTouched) { // Track count uint trackCount=(uint)_TrackVector.size(); // Track count empty ? if (trackCount==0) return 0.f; // Look for the lowest _BeginTime=_TrackVector[0]->getBeginTime (); // Scan all keys for (uint t=1; tgetBeginTime ()<_BeginTime) _BeginTime=_TrackVector[t]->getBeginTime (); } _BeginTimeTouched = false; } return _BeginTime; } // *************************************************************************** TAnimationTime CAnimation::getEndTime () const { NL3D_HAUTO_UI_ANIMATION; if (_EndTimeTouched) { // Track count uint trackCount=(uint)_TrackVector.size(); // Track count empty ? if (trackCount==0) return 0.f; // Look for the highest _EndTime=_TrackVector[0]->getEndTime (); // Scan tracks keys for (uint t=1; tgetEndTime ()>_EndTime) _EndTime=_TrackVector[t]->getEndTime (); } // Check min end time if (_EndTime < _MinEndTime) _EndTime = _MinEndTime; _EndTimeTouched = false; } return _EndTime; } // *************************************************************************** bool CAnimation::allTrackLoop() const { NL3D_HAUTO_UI_ANIMATION; if(_AnimLoopTouched) { // Track count uint trackCount=(uint)_TrackVector.size(); // Default is true _AnimLoop= true; // Scan tracks keys for (uint t=0; tgetLoopMode()) { _AnimLoop= false; break; } } _AnimLoopTouched = false; } return _AnimLoop; } // *************************************************************************** UTrack* CAnimation::getTrackByName (const char* name) { NL3D_HAUTO_UI_ANIMATION; // Get track id uint id=getIdTrackByName (name); // Not found ? if (id==CAnimation::NotFound) // Error, return NULL return NULL; else // No error, return the track return getTrack (id); } // *************************************************************************** void CAnimation::releaseTrack (UTrack* /* track */) { NL3D_HAUTO_UI_ANIMATION; // Nothing to do } // *************************************************************************** void CAnimation::setMinEndTime (TAnimationTime minEndTime) { _MinEndTime = minEndTime; } // *************************************************************************** UAnimation* UAnimation::createAnimation (const char* sPath) { NL3D_HAUTO_UI_ANIMATION; // Allocate an animation CUniquePtr anim (new CAnimation); // Read it NLMISC::CIFile file; if (file.open ( NLMISC::CPath::lookup( sPath ) ) ) { // Serial the animation file.serial (*anim); // Return pointer CAnimation *ret=anim.release (); // Return the animation interface return ret; } else return NULL; } // *************************************************************************** void UAnimation::releaseAnimation (UAnimation* animation) { NL3D_HAUTO_UI_ANIMATION; // Cast the pointer CAnimation* release=(CAnimation*)animation; // Delete it delete release; } // *************************************************************************** void CAnimation::applySampleDivisor(uint sampleDivisor) { NL3D_HAUTO_UI_ANIMATION; for(uint i=0;i<_TrackVector.size();i++) { ITrack *track= _TrackVector[i]; if(track) track->applySampleDivisor(sampleDivisor); } } // *************************************************************************** void CAnimation::applyTrackQuatHeaderCompression() { NL3D_HAUTO_UI_ANIMATION; // if the header compression has already been donne, no op if(_TrackSamplePack) return; // **** First pass: count the number of keys to allocate CTrackSampleCounter sampleCounter; bool someTrackOK= false; for(uint i=0;i<_TrackVector.size();i++) { ITrack *track= _TrackVector[i]; if(track) { // return true only for CTrackSampledQuat if(track->applyTrackQuatHeaderCompressionPass0(sampleCounter)) someTrackOK= true; } } // **** second pass: fill, onlmy if some track matchs (fails for instance for big animations) if(someTrackOK) { uint i; // must create the Sample packer _TrackSamplePack= new CTrackSamplePack; // just copy the built track headers _TrackSamplePack->TrackHeaders.resize((uint32)sampleCounter.TrackHeaders.size()); for(i=0;i<_TrackSamplePack->TrackHeaders.size();i++) { _TrackSamplePack->TrackHeaders[i]= sampleCounter.TrackHeaders[i]; } // and allocate keys _TrackSamplePack->Times.resize(sampleCounter.NumKeys); _TrackSamplePack->Keys.resize(sampleCounter.NumKeys); // start the counter for Pass1 to work uint globalKeyOffset= 0; // fill it for each track for(i=0;i<_TrackVector.size();i++) { ITrack *track= _TrackVector[i]; if(track) { ITrack *newTrack= track->applyTrackQuatHeaderCompressionPass1(globalKeyOffset, *_TrackSamplePack); // if compressed if(newTrack) { // delete the old track, and replace with compressed one delete _TrackVector[i]; _TrackVector[i]= newTrack; } } } nlassert(globalKeyOffset == _TrackSamplePack->Keys.size()); } } // *************************************************************************** struct CTempTrackInfo { uint16 ChannelId; ITrack *Track; bool operator<(const CTempTrackInfo &o) const { return ChannelId < o.ChannelId; } }; void CAnimation::applyAnimHeaderCompression(CAnimationSet *animationSetOwner, const std::map &channelMap) { uint i; nlassert(animationSetOwner); // must not be already done nlassert(_IdByChannelId.empty()); // fill the track info, with Track std::vector tempTrackInfo; tempTrackInfo.resize(_TrackVector.size()); for(i=0;i::const_iterator itChan= channelMap.find(it->first); nlassert(itChan!=channelMap.end()); // store the channelId in the associated track tempTrackInfo[it->second].ChannelId= (uint16)itChan->second; } // sort by channelId sort(tempTrackInfo.begin(), tempTrackInfo.end()); // refill the TrackVector (sorted) _IdByChannelId.resize( tempTrackInfo.size() ); for(i=0;i