514 lines
12 KiB
C++
514 lines
12 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 "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;
|
|
|
|
|
|
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 ((uint32)'_LEN');
|
|
f.serialCheck ((uint32)'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<std::string>& 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; t<trackCount; t++)
|
|
{
|
|
if (_TrackVector[t]->getBeginTime ()<_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; t<trackCount; t++)
|
|
{
|
|
if (_TrackVector[t]->getEndTime ()>_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; t<trackCount; t++)
|
|
{
|
|
if (!_TrackVector[t]->getLoopMode())
|
|
{
|
|
_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
|
|
std::auto_ptr<CAnimation> 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 <std::string, uint32> &channelMap)
|
|
{
|
|
uint i;
|
|
nlassert(animationSetOwner);
|
|
// must not be already done
|
|
nlassert(_IdByChannelId.empty());
|
|
|
|
// fill the track info, with Track
|
|
std::vector<CTempTrackInfo> tempTrackInfo;
|
|
tempTrackInfo.resize(_TrackVector.size());
|
|
for(i=0;i<tempTrackInfo.size();i++)
|
|
{
|
|
tempTrackInfo[i].Track= _TrackVector[i];
|
|
}
|
|
|
|
// fill the track info, with ChannelId
|
|
TMapStringUInt::iterator it;
|
|
for(it= _IdByName.begin();it!=_IdByName.end();it++)
|
|
{
|
|
// search this track in the channelMap
|
|
std::map <std::string, uint32>::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<tempTrackInfo.size();i++)
|
|
{
|
|
_TrackVector[i]= tempTrackInfo[i].Track;
|
|
_IdByChannelId[i]= tempTrackInfo[i].ChannelId;
|
|
}
|
|
|
|
// clear the no more needed track map
|
|
contReset(_IdByName);
|
|
|
|
// must keep the animSet
|
|
_AnimationSetOwner= animationSetOwner;
|
|
}
|
|
|
|
|
|
// ***************************************************************************
|
|
uint CAnimation::getIdTrackByChannelId (uint16 channelId) const
|
|
{
|
|
if(_IdByChannelId.empty())
|
|
return CAnimation::NotFound;
|
|
else
|
|
{
|
|
uint trackId= searchLowerBound(_IdByChannelId, channelId);
|
|
// verify that the channel is really found
|
|
if(_IdByChannelId[trackId]==channelId)
|
|
{
|
|
return trackId;
|
|
}
|
|
else
|
|
return CAnimation::NotFound;
|
|
}
|
|
}
|
|
|
|
// ***************************************************************************
|
|
void CAnimation::addSSSShape(const std::string &shape)
|
|
{
|
|
_SSSShapes.push_back(shape);
|
|
}
|
|
|
|
|
|
} // NL3D
|