// 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_set.h" #include "nel/3d/driver.h" #include "nel/3d/shape_bank.h" #include "nel/misc/stream.h" #include "nel/misc/path.h" #include "nel/misc/file.h" #include "nel/misc/algo.h" using namespace std; using namespace NLMISC; namespace NL3D { // *************************************************************************** CAnimationSet::CAnimationSet (bool headerOptim) { _SampleDivisor= 1; _AnimHeaderOptimisation= headerOptim; _Built= false; } // *************************************************************************** CAnimationSet::~CAnimationSet () { // Erase all animations. for (uint a=0; a<_Animation.size(); a++) delete _Animation[a]; for (uint s=0; s<_SkeletonWeight.size(); s++) delete _SkeletonWeight[s]; } // *************************************************************************** uint CAnimationSet::getNumChannelId () const { return (uint)_ChannelIdByName.size (); } // *************************************************************************** uint CAnimationSet::addAnimation (const char* name, CAnimation* animation) { // error to add an animation after a build() if the animation set is in HeaderCompress mode nlassert(! (_Built && _AnimHeaderOptimisation) ); // if sampleDivisor, apply to the animation if(_SampleDivisor>1) animation->applySampleDivisor(_SampleDivisor); // compress CTrackSampledQuat header if(_AnimHeaderOptimisation) animation->applyTrackQuatHeaderCompression(); // Add an animation _Animation.push_back (animation); _AnimationName.push_back (name); // Add an entry name / animation _AnimationIdByName.insert (std::map ::value_type (name, (uint32)_Animation.size()-1)); // Return animation id return (uint)_Animation.size()-1; } // *************************************************************************** uint CAnimationSet::addSkeletonWeight (const char* name, CSkeletonWeight* skeletonWeight) { // Add an animation _SkeletonWeight.push_back (skeletonWeight); _SkeletonWeightName.push_back (name); // Add an entry name / animation _SkeletonWeightIdByName.insert (std::map ::value_type (name, (uint32)_SkeletonWeight.size()-1)); // Return animation id return (uint)_SkeletonWeight.size()-1; } // *************************************************************************** void CAnimationSet::reset () { _Animation.clear(); _SkeletonWeight.clear(); _ChannelName.clear(); _AnimationName.clear(); _SkeletonWeightName.clear(); _ChannelIdByName.clear(); _AnimationIdByName.clear(); _SkeletonWeightIdByName.clear(); _SSSShapes.clear(); } // *************************************************************************** void CAnimationSet::build () { // error to rebuild in if already done while _AnimHeaderOptimisation, // cause applyAnimHeaderCompression() won't work if(_Built && _AnimHeaderOptimisation) return; _Built= true; // Clear the channel map _ChannelName.clear(); _ChannelIdByName.clear (); // Set of names std::set channelNames; // For each animation in the set uint a; for (a=0; a<_Animation.size(); a++) { // Fill the set of channel names getAnimation (a)->getTrackNames (channelNames); } // Add this name in the map with there iD uint id=0; std::set::iterator ite=channelNames.begin (); while (ite!=channelNames.end ()) { // Insert an entry _ChannelIdByName.insert (std::map ::value_type (*ite, id++)); // Next entry ite++; } // build ChannelName From Map buildChannelNameFromMap(); // If the animation set is in HeaderOptim mode, reduce memory load by removing map if(_AnimHeaderOptimisation) { for (uint a=0; a<_Animation.size(); a++) { _Animation[a]->applyAnimHeaderCompression (this, _ChannelIdByName); } } // Build the set of SSS Shapes from each animation for (a=0; a<_Animation.size(); a++) { const std::vector &shapes= _Animation[a]->getSSSShapes(); for(uint s=0;s::iterator it; for(it= _ChannelIdByName.begin();it!=_ChannelIdByName.end();it++) { nlinfo("ANIMYOYO: %3d: %s", it->second, it->first.c_str()); }*/ } // *************************************************************************** void CAnimationSet::serial (NLMISC::IStream& f) { // serial not possible if header optimisation enabled nlassert(!_AnimHeaderOptimisation); // Serial an header f.serialCheck ((uint32)'_LEN'); f.serialCheck ((uint32)'MINA'); f.serialCheck ((uint32)'TES_'); // Serial a version uint ver= f.serialVersion (1); // Serial the class f.serialContPtr (_Animation); f.serialContPtr (_SkeletonWeight); f.serialCont (_AnimationName); f.serialCont (_SkeletonWeightName); f.serialCont(_ChannelIdByName); f.serialCont(_AnimationIdByName); f.serialCont(_SkeletonWeightIdByName); if(ver>=1) f.serialCont(_ChannelName); else buildChannelNameFromMap(); } // *************************************************************************** bool CAnimationSet::loadFromFiles(const std::string &path, bool recurse /* = true*/, const char *ext /*= "anim"*/, bool wantWarningMessage /*= true*/) { bool everythingOk = true; std::vector anims; NLMISC::CPath::getPathContent(path, recurse, false, true, anims); for (uint k = 0; k < anims.size(); ++k) { std::string fileExt = NLMISC::CFile::getExtension(anims[k]); if (fileExt == ext) // an animation file ? { try { NLMISC::CIFile iFile; iFile.open(anims[k]); std::auto_ptr anim(new CAnimation); anim->serial(iFile); addAnimation(NLMISC::CFile::getFilenameWithoutExtension(anims[k]).c_str(), anim.release()); iFile.close(); } catch (NLMISC::EStream &e) { if (wantWarningMessage) { nlinfo("Unable to load an automatic animation : %s", e.what()); } everythingOk = false; } } } build(); return everythingOk; } // *************************************************************************** void CAnimationSet::setAnimationSampleDivisor(uint sampleDivisor) { _SampleDivisor= sampleDivisor; // 0 is invalid if(_SampleDivisor==0) _SampleDivisor= 1; } // *************************************************************************** uint CAnimationSet::getAnimationSampleDivisor() const { return _SampleDivisor; } // *************************************************************************** void CAnimationSet::buildChannelNameFromMap() { contReset(_ChannelName); _ChannelName.resize(_ChannelIdByName.size()); std::map ::iterator it; for(it= _ChannelIdByName.begin();it!=_ChannelIdByName.end();it++) { _ChannelName[it->second]= it->first; } } // *************************************************************************** void CAnimationSet::preloadSSSShapes(IDriver &drv, CShapeBank &shapeBank) { const std::string shapeCacheName= "SSS_PreLoad"; // Create the Animation Set Shape cache if do not exist if(!shapeBank.isShapeCache(shapeCacheName)) { // allow "inifinite" number of preloaded shapes shapeBank.addShapeCache(shapeCacheName); shapeBank.setShapeCacheSize(shapeCacheName, 1000000); } // For all files std::set::iterator it; for(it=_SSSShapes.begin();it!=_SSSShapes.end();it++) { string fileName= toLower(*it); // link the shape to the shapeCache shapeBank.linkShapeToShapeCache(fileName, shapeCacheName); // If !present in the shapeBank if( shapeBank.getPresentState(fileName)==CShapeBank::NotPresent ) { // Don't load it if no more space in the cache if( shapeBank.getShapeCacheFreeSpace(shapeCacheName)>0 ) { // load it. shapeBank.load(fileName); // If success if( shapeBank.getPresentState(fileName)==CShapeBank::Present ) { // When a shape is first added to the bank, it is not in the cache. // add it and release it to force it to be in the cache. IShape *shp= shapeBank.addRef(fileName); if(shp) { //nlinfo("Loading %s", CPath::lookup(fileName.c_str(), false, false).c_str()); shp->flushTextures(drv, 0); shapeBank.release(shp); } } } } } } } // NL3D