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) 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/>.
22 #include "nel/3d/animation_set.h"
23 #include "nel/3d/driver.h"
24 #include "nel/3d/shape_bank.h"
25 #include "nel/misc/stream.h"
26 #include "nel/misc/path.h"
27 #include "nel/misc/file.h"
28 #include "nel/misc/algo.h"
33 using namespace NLMISC
;
42 // ***************************************************************************
43 CAnimationSet::CAnimationSet (bool headerOptim
)
46 _AnimHeaderOptimisation
= headerOptim
;
50 // ***************************************************************************
51 CAnimationSet::~CAnimationSet ()
53 // Erase all animations.
54 for (uint a
=0; a
<_Animation
.size(); a
++)
56 for (uint s
=0; s
<_SkeletonWeight
.size(); s
++)
57 delete _SkeletonWeight
[s
];
60 // ***************************************************************************
61 uint
CAnimationSet::getNumChannelId () const
63 return (uint
)_ChannelIdByName
.size ();
66 // ***************************************************************************
67 uint
CAnimationSet::addAnimation (const char* name
, CAnimation
* animation
)
69 // error to add an animation after a build() if the animation set is in HeaderCompress mode
70 nlassert(! (_Built
&& _AnimHeaderOptimisation
) );
72 // if sampleDivisor, apply to the animation
74 animation
->applySampleDivisor(_SampleDivisor
);
76 // compress CTrackSampledQuat header
77 if(_AnimHeaderOptimisation
)
78 animation
->applyTrackQuatHeaderCompression();
81 _Animation
.push_back (animation
);
82 _AnimationName
.push_back (name
);
84 // Add an entry name / animation
85 _AnimationIdByName
.insert (std::map
<std::string
, uint32
>::value_type (name
, (uint32
)_Animation
.size()-1));
87 // Return animation id
88 return (uint
)_Animation
.size()-1;
91 // ***************************************************************************
92 uint
CAnimationSet::addSkeletonWeight (const char* name
, CSkeletonWeight
* skeletonWeight
)
95 _SkeletonWeight
.push_back (skeletonWeight
);
96 _SkeletonWeightName
.push_back (name
);
98 // Add an entry name / animation
99 _SkeletonWeightIdByName
.insert (std::map
<std::string
, uint32
>::value_type (name
, (uint32
)_SkeletonWeight
.size()-1));
101 // Return animation id
102 return (uint
)_SkeletonWeight
.size()-1;
105 // ***************************************************************************
106 void CAnimationSet::reset ()
109 _SkeletonWeight
.clear();
110 _ChannelName
.clear();
111 _AnimationName
.clear();
112 _SkeletonWeightName
.clear();
113 _ChannelIdByName
.clear();
114 _AnimationIdByName
.clear();
115 _SkeletonWeightIdByName
.clear();
119 // ***************************************************************************
120 void CAnimationSet::build ()
122 // error to rebuild in if already done while _AnimHeaderOptimisation,
123 // cause applyAnimHeaderCompression() won't work
124 if(_Built
&& _AnimHeaderOptimisation
)
128 // Clear the channel map
129 _ChannelName
.clear();
130 _ChannelIdByName
.clear ();
133 std::set
<std::string
> channelNames
;
135 // For each animation in the set
137 for (a
=0; a
<_Animation
.size(); a
++)
139 // Fill the set of channel names
140 getAnimation (a
)->getTrackNames (channelNames
);
143 // Add this name in the map with there iD
145 std::set
<std::string
>::iterator ite
=channelNames
.begin ();
146 while (ite
!=channelNames
.end ())
149 _ChannelIdByName
.insert (std::map
<std::string
, uint32
>::value_type (*ite
, id
++));
155 // build ChannelName From Map
156 buildChannelNameFromMap();
158 // If the animation set is in HeaderOptim mode, reduce memory load by removing map<string, trackId>
159 if(_AnimHeaderOptimisation
)
161 for (uint a
=0; a
<_Animation
.size(); a
++)
163 _Animation
[a
]->applyAnimHeaderCompression (this, _ChannelIdByName
);
167 // Build the set of SSS Shapes from each animation
168 for (a
=0; a
<_Animation
.size(); a
++)
170 const std::vector
<std::string
> &shapes
= _Animation
[a
]->getSSSShapes();
171 for(uint s
=0;s
<shapes
.size();s
++)
173 // insert (may be already done)
174 _SSSShapes
.insert(shapes
[s
]);
180 /*nlinfo("ANIMYOYO: %d channels", _ChannelIdByName.size());
181 std::map <std::string, uint32>::iterator it;
182 for(it= _ChannelIdByName.begin();it!=_ChannelIdByName.end();it++)
184 nlinfo("ANIMYOYO: %3d: %s", it->second, it->first.c_str());
188 // ***************************************************************************
189 void CAnimationSet::serial (NLMISC::IStream
& f
)
191 // serial not possible if header optimisation enabled
192 nlassert(!_AnimHeaderOptimisation
);
195 f
.serialCheck (NELID("_LEN"));
196 f
.serialCheck (NELID("MINA"));
197 f
.serialCheck (NELID("TES_"));
200 uint ver
= f
.serialVersion (1);
203 f
.serialContPtr (_Animation
);
204 f
.serialContPtr (_SkeletonWeight
);
205 f
.serialCont (_AnimationName
);
206 f
.serialCont (_SkeletonWeightName
);
207 f
.serialCont(_ChannelIdByName
);
208 f
.serialCont(_AnimationIdByName
);
209 f
.serialCont(_SkeletonWeightIdByName
);
211 f
.serialCont(_ChannelName
);
213 buildChannelNameFromMap();
216 // ***************************************************************************
217 bool CAnimationSet::loadFromFiles(const std::string
&path
, bool recurse
/* = true*/, const char *ext
/*= "anim"*/, bool wantWarningMessage
/*= true*/)
219 bool everythingOk
= true;
220 std::vector
<std::string
> anims
;
221 NLMISC::CPath::getPathContent(path
, recurse
, false, true, anims
);
222 for (uint k
= 0; k
< anims
.size(); ++k
)
224 std::string fileExt
= NLMISC::CFile::getExtension(anims
[k
]);
225 if (fileExt
== ext
) // an animation file ?
229 NLMISC::CIFile iFile
;
230 iFile
.open(anims
[k
]);
231 CUniquePtr
<CAnimation
> anim(new CAnimation
);
233 addAnimation(NLMISC::CFile::getFilenameWithoutExtension(anims
[k
]).c_str(), anim
.release());
237 catch (const NLMISC::EStream
&e
)
239 if (wantWarningMessage
)
241 nlinfo("Unable to load an automatic animation : %s", e
.what());
243 everythingOk
= false;
251 // ***************************************************************************
252 void CAnimationSet::setAnimationSampleDivisor(uint sampleDivisor
)
254 _SampleDivisor
= sampleDivisor
;
256 if(_SampleDivisor
==0)
260 // ***************************************************************************
261 uint
CAnimationSet::getAnimationSampleDivisor() const
263 return _SampleDivisor
;
266 // ***************************************************************************
267 void CAnimationSet::buildChannelNameFromMap()
269 contReset(_ChannelName
);
270 _ChannelName
.resize(_ChannelIdByName
.size());
271 std::map
<std::string
, uint32
>::iterator it
;
272 for(it
= _ChannelIdByName
.begin();it
!=_ChannelIdByName
.end();it
++)
274 _ChannelName
[it
->second
]= it
->first
;
278 // ***************************************************************************
279 void CAnimationSet::preloadSSSShapes(IDriver
&drv
, CShapeBank
&shapeBank
)
281 const std::string shapeCacheName
= "SSS_PreLoad";
283 // Create the Animation Set Shape cache if do not exist
284 if(!shapeBank
.isShapeCache(shapeCacheName
))
286 // allow "inifinite" number of preloaded shapes
287 shapeBank
.addShapeCache(shapeCacheName
);
288 shapeBank
.setShapeCacheSize(shapeCacheName
, 1000000);
292 std::set
<std::string
>::iterator it
;
293 for(it
=_SSSShapes
.begin();it
!=_SSSShapes
.end();it
++)
295 string fileName
= toLowerAscii(*it
);
297 // link the shape to the shapeCache
298 shapeBank
.linkShapeToShapeCache(fileName
, shapeCacheName
);
300 // If !present in the shapeBank
301 if( shapeBank
.getPresentState(fileName
)==CShapeBank::NotPresent
)
303 // Don't load it if no more space in the cache
304 if( shapeBank
.getShapeCacheFreeSpace(shapeCacheName
)>0 )
307 shapeBank
.load(fileName
);
310 if( shapeBank
.getPresentState(fileName
)==CShapeBank::Present
)
312 // When a shape is first added to the bank, it is not in the cache.
313 // add it and release it to force it to be in the cache.
314 IShape
*shp
= shapeBank
.addRef(fileName
);
317 //nlinfo("Loading %s", CPath::lookup(fileName.c_str(), false, false).c_str());
318 shp
->flushTextures(drv
, 0);
319 shapeBank
.release(shp
);